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