| <?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="en"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../../jacoco-resources/report.gif" type="image/gif"/><title>DefaultHashFormatFactory.java</title><link rel="stylesheet" href="../../jacoco-resources/prettify.css" type="text/css"/><script type="text/javascript" src="../../jacoco-resources/prettify.js"></script></head><body onload="window['PR_TAB_WIDTH']=4;prettyPrint()"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="../../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../../index.html" class="el_report">Apache Shiro :: Test Coverage</a> > <a href="../index.html" class="el_bundle">shiro-crypto-hash</a> > <a href="index.source.html" class="el_package">org.apache.shiro.crypto.hash.format</a> > <span class="el_source">DefaultHashFormatFactory.java</span></div><h1>DefaultHashFormatFactory.java</h1><pre class="source lang-java linenums">/* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.shiro.crypto.hash.format; |
| |
| import org.apache.shiro.util.ClassUtils; |
| import org.apache.shiro.util.StringUtils; |
| import org.apache.shiro.util.UnknownClassException; |
| |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Map; |
| import java.util.Set; |
| |
| /** |
| * This default {@code HashFormatFactory} implementation heuristically determines a {@code HashFormat} class to |
| * instantiate based on the input argument and returns a new instance of the discovered class. The heuristics are |
| * detailed in the {@link #getInstance(String) getInstance} method documentation. |
| * |
| * @since 1.2 |
| */ |
| public class DefaultHashFormatFactory implements HashFormatFactory { |
| |
| private Map<String, String> formatClassNames; //id - to - fully qualified class name |
| |
| private Set<String> searchPackages; //packages to search for HashFormat implementations |
| |
| <span class="fc" id="L43"> public DefaultHashFormatFactory() {</span> |
| <span class="fc" id="L44"> this.searchPackages = new HashSet<String>();</span> |
| <span class="fc" id="L45"> this.formatClassNames = new HashMap<String, String>();</span> |
| <span class="fc" id="L46"> }</span> |
| |
| /** |
| * Returns a {@code hashFormatAlias}-to-<code>fullyQualifiedHashFormatClassNameImplementation</code> map. |
| * <p/> |
| * This map will be used by the {@link #getInstance(String) getInstance} implementation: that method's argument |
| * will be used as a lookup key to this map. If the map returns a value, that value will be used to instantiate |
| * and return a new {@code HashFormat} instance. |
| * <h3>Defaults</h3> |
| * Shiro's default HashFormat implementations (as listed by the {@link ProvidedHashFormat} enum) will |
| * be searched automatically independently of this map. You only need to populate this map with custom |
| * {@code HashFormat} implementations that are <em>not</em> already represented by a {@code ProvidedHashFormat}. |
| * <h3>Efficiency</h3> |
| * Populating this map will be more efficient than configuring {@link #getSearchPackages() searchPackages}, |
| * but search packages may be more convenient depending on the number of {@code HashFormat} implementations that |
| * need to be supported by this factory. |
| * |
| * @return a {@code hashFormatAlias}-to-<code>fullyQualifiedHashFormatClassNameImplementation</code> map. |
| */ |
| public Map<String, String> getFormatClassNames() { |
| <span class="fc" id="L66"> return formatClassNames;</span> |
| } |
| |
| /** |
| * Sets the {@code hash-format-alias}-to-{@code fullyQualifiedHashFormatClassNameImplementation} map to be used in |
| * the {@link #getInstance(String)} implementation. See the {@link #getFormatClassNames()} JavaDoc for more |
| * information. |
| * <h3>Efficiency</h3> |
| * Populating this map will be more efficient than configuring {@link #getSearchPackages() searchPackages}, |
| * but search packages may be more convenient depending on the number of {@code HashFormat} implementations that |
| * need to be supported by this factory. |
| * |
| * @param formatClassNames the {@code hash-format-alias}-to-{@code fullyQualifiedHashFormatClassNameImplementation} |
| * map to be used in the {@link #getInstance(String)} implementation. |
| */ |
| public void setFormatClassNames(Map<String, String> formatClassNames) { |
| <span class="fc" id="L82"> this.formatClassNames = formatClassNames;</span> |
| <span class="fc" id="L83"> }</span> |
| |
| /** |
| * Returns a set of package names that can be searched for {@link HashFormat} implementations according to |
| * heuristics defined in the {@link #getHashFormatClass(String, String) getHashFormat(packageName, token)} JavaDoc. |
| * <h3>Efficiency</h3> |
| * Configuring this property is not as efficient as configuring a {@link #getFormatClassNames() formatClassNames} |
| * map, but it may be more convenient depending on the number of {@code HashFormat} implementations that |
| * need to be supported by this factory. |
| * |
| * @return a set of package names that can be searched for {@link HashFormat} implementations |
| * @see #getHashFormatClass(String, String) |
| */ |
| public Set<String> getSearchPackages() { |
| <span class="fc" id="L97"> return searchPackages;</span> |
| } |
| |
| /** |
| * Sets a set of package names that can be searched for {@link HashFormat} implementations according to |
| * heuristics defined in the {@link #getHashFormatClass(String, String) getHashFormat(packageName, token)} JavaDoc. |
| * <h3>Efficiency</h3> |
| * Configuring this property is not as efficient as configuring a {@link #getFormatClassNames() formatClassNames} |
| * map, but it may be more convenient depending on the number of {@code HashFormat} implementations that |
| * need to be supported by this factory. |
| * |
| * @param searchPackages a set of package names that can be searched for {@link HashFormat} implementations |
| */ |
| public void setSearchPackages(Set<String> searchPackages) { |
| <span class="fc" id="L111"> this.searchPackages = searchPackages;</span> |
| <span class="fc" id="L112"> }</span> |
| |
| public HashFormat getInstance(String in) { |
| <span class="fc bfc" id="L115" title="All 2 branches covered."> if (in == null) {</span> |
| <span class="fc" id="L116"> return null;</span> |
| } |
| |
| <span class="fc" id="L119"> HashFormat hashFormat = null;</span> |
| <span class="fc" id="L120"> Class clazz = null;</span> |
| |
| //NOTE: this code block occurs BEFORE calling getHashFormatClass(in) on purpose as a performance |
| //optimization. If the input arg is an MCF-formatted string, there will be many unnecessary ClassLoader |
| //misses which can be slow. By checking the MCF-formatted option, we can significantly improve performance |
| <span class="fc bfc" id="L125" title="All 2 branches covered."> if (in.startsWith(ModularCryptFormat.TOKEN_DELIMITER)) {</span> |
| //odds are high that the input argument is not a fully qualified class name or a format key (e.g. 'hex', |
| //base64' or 'shiro1'). Try to find the key and lookup via that: |
| <span class="fc" id="L128"> String test = in.substring(ModularCryptFormat.TOKEN_DELIMITER.length());</span> |
| <span class="fc" id="L129"> String[] tokens = test.split("\\" + ModularCryptFormat.TOKEN_DELIMITER);</span> |
| //the MCF ID is always the first token in the delimited string: |
| <span class="pc bpc" id="L131" title="2 of 4 branches missed."> String possibleMcfId = (tokens != null && tokens.length > 0) ? tokens[0] : null;</span> |
| <span class="pc bpc" id="L132" title="1 of 2 branches missed."> if (possibleMcfId != null) {</span> |
| //found a possible MCF ID - test it using our heuristics to see if we can find a corresponding class: |
| <span class="fc" id="L134"> clazz = getHashFormatClass(possibleMcfId);</span> |
| } |
| } |
| |
| <span class="fc bfc" id="L138" title="All 2 branches covered."> if (clazz == null) {</span> |
| //not an MCF-formatted string - use the unaltered input arg and go through our heuristics: |
| <span class="fc" id="L140"> clazz = getHashFormatClass(in);</span> |
| } |
| |
| <span class="fc bfc" id="L143" title="All 2 branches covered."> if (clazz != null) {</span> |
| //we found a HashFormat class - instantiate it: |
| <span class="fc" id="L145"> hashFormat = newHashFormatInstance(clazz);</span> |
| } |
| |
| <span class="fc" id="L148"> return hashFormat;</span> |
| } |
| |
| /** |
| * Heuristically determine the fully qualified HashFormat implementation class name based on the specified |
| * token. |
| * <p/> |
| * This implementation functions as follows (in order): |
| * <ol> |
| * <li>See if the argument can be used as a lookup key in the {@link #getFormatClassNames() formatClassNames} |
| * map. If a value (a fully qualified class name {@link HashFormat HashFormat} implementation) is found, |
| * {@link ClassUtils#forName(String) lookup} the class and return it.</li> |
| * <li> |
| * Check to see if the token argument is a |
| * {@link ProvidedHashFormat} enum value. If so, acquire the corresponding {@code HashFormat} class and |
| * return it. |
| * </li> |
| * <li> |
| * Check to see if the token argument is itself a fully qualified class name. If so, try to load the class |
| * and return it. |
| * </li> |
| * <li>If the above options do not result in a discovered class, search all all configured |
| * {@link #getSearchPackages() searchPackages} using heuristics defined in the |
| * {@link #getHashFormatClass(String, String) getHashFormatClass(packageName, token)} method documentation |
| * (relaying the {@code token} argument to that method for each configured package). |
| * </li> |
| * </ol> |
| * <p/> |
| * If a class is not discovered via any of the above means, {@code null} is returned to indicate the class |
| * could not be found. |
| * |
| * @param token the string token from which a class name will be heuristically determined. |
| * @return the discovered HashFormat class implementation or {@code null} if no class could be heuristically determined. |
| */ |
| protected Class getHashFormatClass(String token) { |
| |
| <span class="fc" id="L184"> Class clazz = null;</span> |
| |
| //check to see if the token is a configured FQCN alias. This is faster than searching packages, |
| //so we try this first: |
| <span class="pc bpc" id="L188" title="1 of 2 branches missed."> if (this.formatClassNames != null) {</span> |
| <span class="fc" id="L189"> String value = this.formatClassNames.get(token);</span> |
| <span class="fc bfc" id="L190" title="All 2 branches covered."> if (value != null) {</span> |
| //found an alias - see if the value is a class: |
| <span class="fc" id="L192"> clazz = lookupHashFormatClass(value);</span> |
| } |
| } |
| |
| //check to see if the token is one of Shiro's provided FQCN aliases (again, faster than searching): |
| <span class="fc bfc" id="L197" title="All 2 branches covered."> if (clazz == null) {</span> |
| <span class="fc" id="L198"> ProvidedHashFormat provided = ProvidedHashFormat.byId(token);</span> |
| <span class="fc bfc" id="L199" title="All 2 branches covered."> if (provided != null) {</span> |
| <span class="fc" id="L200"> clazz = provided.getHashFormatClass();</span> |
| } |
| } |
| |
| <span class="fc bfc" id="L204" title="All 2 branches covered."> if (clazz == null) {</span> |
| //check to see if 'token' was a FQCN itself: |
| <span class="fc" id="L206"> clazz = lookupHashFormatClass(token);</span> |
| } |
| |
| <span class="fc bfc" id="L209" title="All 2 branches covered."> if (clazz == null) {</span> |
| //token wasn't a FQCN or a FQCN alias - try searching in configured packages: |
| <span class="pc bpc" id="L211" title="1 of 2 branches missed."> if (this.searchPackages != null) {</span> |
| <span class="fc bfc" id="L212" title="All 2 branches covered."> for (String packageName : this.searchPackages) {</span> |
| <span class="fc" id="L213"> clazz = getHashFormatClass(packageName, token);</span> |
| <span class="fc bfc" id="L214" title="All 2 branches covered."> if (clazz != null) {</span> |
| //found it: |
| <span class="fc" id="L216"> break;</span> |
| } |
| <span class="fc" id="L218"> }</span> |
| } |
| } |
| |
| <span class="fc bfc" id="L222" title="All 2 branches covered."> if (clazz != null) {</span> |
| <span class="fc" id="L223"> assertHashFormatImpl(clazz);</span> |
| } |
| |
| <span class="fc" id="L226"> return clazz;</span> |
| } |
| |
| /** |
| * Heuristically determine the fully qualified {@code HashFormat} implementation class name in the specified |
| * package based on the provided token. |
| * <p/> |
| * The token is expected to be a relevant fragment of an unqualified class name in the specified package. |
| * A 'relevant fragment' can be one of the following: |
| * <ul> |
| * <li>The {@code HashFormat} implementation unqualified class name</li> |
| * <li>The prefix of an unqualified class name ending with the text {@code Format}. The first character of |
| * this prefix can be upper or lower case and both options will be tried.</li> |
| * <li>The prefix of an unqualified class name ending with the text {@code HashFormat}. The first character of |
| * this prefix can be upper or lower case and both options will be tried.</li> |
| * <li>The prefix of an unqualified class name ending with the text {@code CryptoFormat}. The first character |
| * of this prefix can be upper or lower case and both options will be tried.</li> |
| * </ul> |
| * <p/> |
| * Some examples: |
| * <table> |
| * <tr> |
| * <th>Package Name</th> |
| * <th>Token</th> |
| * <th>Expected Output Class</th> |
| * <th>Notes</th> |
| * </tr> |
| * <tr> |
| * <td>{@code com.foo.whatever}</td> |
| * <td>{@code MyBarFormat}</td> |
| * <td>{@code com.foo.whatever.MyBarFormat}</td> |
| * <td>Token is a complete unqualified class name</td> |
| * </tr> |
| * <tr> |
| * <td>{@code com.foo.whatever}</td> |
| * <td>{@code Bar}</td> |
| * <td>{@code com.foo.whatever.BarFormat} <em>or</em> {@code com.foo.whatever.BarHashFormat} <em>or</em> |
| * {@code com.foo.whatever.BarCryptFormat}</td> |
| * <td>The token is only part of the unqualified class name - i.e. all characters in front of the {@code *Format} |
| * {@code *HashFormat} or {@code *CryptFormat} suffix. Note that the {@code *Format} variant will be tried before |
| * {@code *HashFormat} and then finally {@code *CryptFormat}</td> |
| * </tr> |
| * <tr> |
| * <td>{@code com.foo.whatever}</td> |
| * <td>{@code bar}</td> |
| * <td>{@code com.foo.whatever.BarFormat} <em>or</em> {@code com.foo.whatever.BarHashFormat} <em>or</em> |
| * {@code com.foo.whatever.BarCryptFormat}</td> |
| * <td>Exact same output as the above {@code Bar} input example. (The token differs only by the first character)</td> |
| * </tr> |
| * </table> |
| * |
| * @param packageName the package to search for matching {@code HashFormat} implementations. |
| * @param token the string token from which a class name will be heuristically determined. |
| * @return the discovered HashFormat class implementation or {@code null} if no class could be heuristically determined. |
| */ |
| protected Class getHashFormatClass(String packageName, String token) { |
| <span class="fc" id="L282"> String test = token;</span> |
| <span class="fc" id="L283"> Class clazz = null;</span> |
| <span class="pc bpc" id="L284" title="1 of 2 branches missed."> String pkg = packageName == null ? "" : packageName;</span> |
| |
| //1. Assume the arg is a fully qualified class name in the classpath: |
| <span class="fc" id="L287"> clazz = lookupHashFormatClass(test);</span> |
| |
| <span class="pc bpc" id="L289" title="1 of 2 branches missed."> if (clazz == null) {</span> |
| <span class="fc" id="L290"> test = pkg + "." + token;</span> |
| <span class="fc" id="L291"> clazz = lookupHashFormatClass(test);</span> |
| } |
| |
| <span class="pc bpc" id="L294" title="1 of 2 branches missed."> if (clazz == null) {</span> |
| <span class="fc" id="L295"> test = pkg + "." + StringUtils.uppercaseFirstChar(token) + "Format";</span> |
| <span class="fc" id="L296"> clazz = lookupHashFormatClass(test);</span> |
| } |
| |
| <span class="pc bpc" id="L299" title="1 of 2 branches missed."> if (clazz == null) {</span> |
| <span class="fc" id="L300"> test = pkg + "." + token + "Format";</span> |
| <span class="fc" id="L301"> clazz = lookupHashFormatClass(test);</span> |
| } |
| |
| <span class="pc bpc" id="L304" title="1 of 2 branches missed."> if (clazz == null) {</span> |
| <span class="fc" id="L305"> test = pkg + "." + StringUtils.uppercaseFirstChar(token) + "HashFormat";</span> |
| <span class="fc" id="L306"> clazz = lookupHashFormatClass(test);</span> |
| } |
| |
| <span class="fc bfc" id="L309" title="All 2 branches covered."> if (clazz == null) {</span> |
| <span class="fc" id="L310"> test = pkg + "." + token + "HashFormat";</span> |
| <span class="fc" id="L311"> clazz = lookupHashFormatClass(test);</span> |
| } |
| |
| <span class="fc bfc" id="L314" title="All 2 branches covered."> if (clazz == null) {</span> |
| <span class="fc" id="L315"> test = pkg + "." + StringUtils.uppercaseFirstChar(token) + "CryptFormat";</span> |
| <span class="fc" id="L316"> clazz = lookupHashFormatClass(test);</span> |
| } |
| |
| <span class="fc bfc" id="L319" title="All 2 branches covered."> if (clazz == null) {</span> |
| <span class="fc" id="L320"> test = pkg + "." + token + "CryptFormat";</span> |
| <span class="fc" id="L321"> clazz = lookupHashFormatClass(test);</span> |
| } |
| |
| <span class="fc bfc" id="L324" title="All 2 branches covered."> if (clazz == null) {</span> |
| <span class="fc" id="L325"> return null; //ran out of options</span> |
| } |
| |
| <span class="fc" id="L328"> assertHashFormatImpl(clazz);</span> |
| |
| <span class="fc" id="L330"> return clazz;</span> |
| } |
| |
| protected Class lookupHashFormatClass(String name) { |
| try { |
| <span class="fc" id="L335"> return ClassUtils.forName(name);</span> |
| <span class="fc" id="L336"> } catch (UnknownClassException ignored) {</span> |
| } |
| |
| <span class="fc" id="L339"> return null;</span> |
| } |
| |
| protected final void assertHashFormatImpl(Class clazz) { |
| <span class="pc bpc" id="L343" title="1 of 4 branches missed."> if (!HashFormat.class.isAssignableFrom(clazz) || clazz.isInterface()) {</span> |
| <span class="fc" id="L344"> throw new IllegalArgumentException("Discovered class [" + clazz.getName() + "] is not a " +</span> |
| <span class="fc" id="L345"> HashFormat.class.getName() + " implementation.");</span> |
| } |
| |
| <span class="fc" id="L348"> }</span> |
| |
| protected final HashFormat newHashFormatInstance(Class clazz) { |
| <span class="fc" id="L351"> assertHashFormatImpl(clazz);</span> |
| <span class="fc" id="L352"> return (HashFormat) ClassUtils.newInstance(clazz);</span> |
| } |
| } |
| </pre><div class="footer"><span class="right">Created with <a href="http://www.eclemma.org/jacoco">JaCoCo</a> 0.7.7.201606060606</span></div></body></html> |