@SuppressWarnings({"checkstyle:CyclomaticComplexity", "checkstyle:NPathComplexity",
            "checkstyle:MagicNumber", "checkstyle:MethodLength"})
    public static void main(String[] args) {
        CommandLineParser parser = new DefaultParser();

        Options options = new Options();
        options.addOption(HELP).addOption(DEBUG).addOption(ALGORITHM).addOption(ITERATIONS);
        options.addOption(RESOURCE).addOption(PASSWORD).addOption(PASSWORD_NC);
        options.addOption(SALT).addOption(SALT_BYTES).addOption(SALT_GEN).addOption(SALT_GEN_SIZE).addOption(NO_SALT_GEN);
        options.addOption(PRIVATE_SALT).addOption(PRIVATE_SALT_BYTES);
        options.addOption(FORMAT);

        boolean debug = false;
        //user unspecified
        String algorithm = null;
        //0 means unspecified by the end-user
        int iterations = 0;
        boolean resource = false;
        boolean password = false;
        boolean passwordConfirm = true;
        String saltString = null;
        String saltBytesString = null;
        boolean generateSalt = false;
        int generatedSaltSize = DEFAULT_GENERATED_SALT_SIZE;
        String privateSaltString = null;
        String privateSaltBytesString = null;

        String formatString = null;

        char[] passwordChars = null;

        try {
            CommandLine line = parser.parse(options, args);

            if (line.hasOption(HELP.getOpt())) {
                printHelpAndExit(options, null, debug, 0);
            }
            if (line.hasOption(DEBUG.getOpt())) {
                debug = true;
            }
            if (line.hasOption(ALGORITHM.getOpt())) {
                algorithm = line.getOptionValue(ALGORITHM.getOpt());
            }
            if (line.hasOption(ITERATIONS.getOpt())) {
                iterations = getRequiredPositiveInt(line, ITERATIONS);
            }
            if (line.hasOption(PASSWORD.getOpt())) {
                password = true;
                generateSalt = true;
            }
            if (line.hasOption(RESOURCE.getOpt())) {
                resource = true;
            }
            if (line.hasOption(PASSWORD_NC.getOpt())) {
                password = true;
                generateSalt = true;
                passwordConfirm = false;
            }
            if (line.hasOption(SALT.getOpt())) {
                saltString = line.getOptionValue(SALT.getOpt());
            }
            if (line.hasOption(SALT_BYTES.getOpt())) {
                saltBytesString = line.getOptionValue(SALT_BYTES.getOpt());
            }
            if (line.hasOption(NO_SALT_GEN.getOpt())) {
                generateSalt = false;
            }
            if (line.hasOption(SALT_GEN.getOpt())) {
                generateSalt = true;
            }
            if (line.hasOption(SALT_GEN_SIZE.getOpt())) {
                generateSalt = true;
                generatedSaltSize = getRequiredPositiveInt(line, SALT_GEN_SIZE);
                if (generatedSaltSize % 8 != 0) {
                    throw new IllegalArgumentException("Generated salt size must be"
                            + "a multiple of 8 (e.g. 128, 192, 256, 512, etc.).");
                }
            }
            if (line.hasOption(PRIVATE_SALT.getOpt())) {
                privateSaltString = line.getOptionValue(PRIVATE_SALT.getOpt());
            }
            if (line.hasOption(PRIVATE_SALT_BYTES.getOpt())) {
                privateSaltBytesString = line.getOptionValue(PRIVATE_SALT_BYTES.getOpt());
            }
            if (line.hasOption(FORMAT.getOpt())) {
                formatString = line.getOptionValue(FORMAT.getOpt());
            }

            String sourceValue;

            Object source;

            if (password) {
                passwordChars = readPassword(passwordConfirm);
                source = passwordChars;
            } else {
                String[] remainingArgs = line.getArgs();
                if (remainingArgs == null || remainingArgs.length != 1) {
                    printHelpAndExit(options, null, debug, -1);
                }

                assert remainingArgs != null;
                sourceValue = toString(remainingArgs);

                if (resource) {
                    if (!ResourceUtils.hasResourcePrefix(sourceValue)) {
                        source = toFile(sourceValue);
                    } else {
                        source = ResourceUtils.getInputStreamForPath(sourceValue);
                    }
                } else {
                    source = sourceValue;
                }
            }

            if (algorithm == null) {
                if (password) {
                    algorithm = DEFAULT_PASSWORD_ALGORITHM_NAME;
                } else {
                    algorithm = DEFAULT_ALGORITHM_NAME;
                }
            }

            if (iterations < DEFAULT_NUM_ITERATIONS) {
                //Iterations were not specified.  Default to 350,000 when password hashing, and 1 for everything else:
                if (password) {
                    iterations = DEFAULT_PASSWORD_NUM_ITERATIONS;
                } else {
                    iterations = DEFAULT_NUM_ITERATIONS;
                }
            }

            ByteSource publicSalt = getSalt(saltString, saltBytesString, generateSalt, generatedSaltSize);
            // FIXME: add options here.
            HashRequest hashRequest = new SimpleHashRequest(algorithm, ByteSource.Util.bytes(source), publicSalt, emptyMap());

            DefaultHashService hashService = new DefaultHashService();
            Hash hash = hashService.computeHash(hashRequest);

            if (formatString == null) {
                //Output format was not specified.  Default to 'shiro2' when password hashing, and 'hex' for
                //everything else:
                if (password) {
                    formatString = Shiro2CryptFormat.class.getName();
                } else {
                    formatString = getHexFormatString();
                }
            }

            HashFormat format = HASH_FORMAT_FACTORY.getInstance(formatString);

            if (format == null) {
                throw new IllegalArgumentException("Unrecognized hash format '" + formatString + "'.");
            }

            String output = format.format(hash);

            LOG.info(output);

        } catch (IllegalArgumentException iae) {
            exit(iae, debug);
        } catch (UnknownAlgorithmException uae) {
            exit(uae, debug);
        } catch (IOException ioe) {
            exit(ioe, debug);
        } catch (Exception e) {
            printHelpAndExit(options, e, debug, -1);
        } finally {
            if (passwordChars != null && passwordChars.length > 0) {
                for (int i = 0; i < passwordChars.length; i++) {
                    passwordChars[i] = ' ';
                }
            }
        }
    }
Commandline line utility to hash data such as strings, passwords, resources (files, urls, etc.).
<p/>
Usage:
<pre>
java -jar shiro-tools-hasher<em>-version</em>-cli.jar
</pre>
This will print out all supported options with documentation.

@since 1.2
tools/hasher/src/main/java/org/apache/shiro/tools/hasher/Hasher.java