001package com.box.sdk.internal.pool;
002
003import java.security.NoSuchAlgorithmException;
004import java.util.Map;
005import java.util.Queue;
006import java.util.concurrent.ConcurrentHashMap;
007import java.util.concurrent.LinkedBlockingQueue;
008import javax.crypto.Mac;
009
010/**
011 * Reusable thread-safe pool for {@link Mac} instances.
012 *
013 * <p>Example:
014 *
015 * <pre>{@code
016 * Mac mac = macPool.acquire();
017 * try {
018 *     ...
019 * } finally {
020 *     macPool.release(mac);
021 * }
022 *
023 * }</pre>
024 */
025public class MacPool {
026
027  /** Pool of {@link Mac}-s by algorithm. */
028  private final Map<String, Queue<Mac>> macPoolByAlgorithm =
029      new ConcurrentHashMap<String, Queue<Mac>>();
030
031  /** Constructor. */
032  public MacPool() {}
033
034  /**
035   * Acquires reusable {@link Mac}, has to be also released!
036   *
037   * @param algorithm {@link Mac#getAlgorithm()}
038   * @return shared {@link Mac}
039   * @see #release(Mac)
040   */
041  public Mac acquire(String algorithm) {
042    Mac result = null;
043
044    Queue<Mac> pool = this.macPoolByAlgorithm.get(algorithm);
045    if (pool != null) {
046      result = pool.poll();
047    }
048
049    if (result != null) {
050      // it has to be synchronized, for the case that some memory parts of Mac provider are changed,
051      // but not yet visible for this thread
052      synchronized (result) {
053        result.reset();
054      }
055      return result;
056    }
057
058    try {
059      return Mac.getInstance(algorithm);
060    } catch (NoSuchAlgorithmException e) {
061      throw new IllegalArgumentException(String.format("NoSuchAlgorithm '%s':", algorithm), e);
062    }
063  }
064
065  /**
066   * Releases a previously acquired {@link Mac}.
067   *
068   * @param mac for release
069   * @see #acquire(String)
070   */
071  public void release(Mac mac) {
072    Queue<Mac> pool = this.macPoolByAlgorithm.get(mac.getAlgorithm());
073    if (pool == null) {
074      pool = new LinkedBlockingQueue<Mac>();
075      this.macPoolByAlgorithm.put(mac.getAlgorithm(), pool);
076    }
077    pool.offer(mac);
078  }
079}