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