14

I'm doing research on GenericObjectPool by putting Cipher in pool so it can be reused.

GenericObjectPool<Cipher> pool;

CipherFactory factory = new CipherFactory(); 
this.pool = new GenericObjectPool<Cipher>(factory);
pool.setMaxTotal(10);
pool.setBlockWhenExhausted(true);
pool.setMaxWaitMillis(30 * 1000);

CipherFactory

public class CipherFactory extends BasePooledObjectFactory<Cipher> {

    private boolean running = false;

    @Override
    public Cipher create() throws Exception {
        return Cipher.getInstance("DESede/CBC/NoPadding");
    }

    @Override
    public PooledObject<Cipher> wrap(Cipher arg0) {
        return new DefaultPooledObject<Cipher>(arg0);
    }

    @Override
    public boolean validateObject(PooledObject<Cipher> p) {
        //Ensures that the instance is safe to be returned by the pool
        return true;
    }

    @Override
    public void destroyObject(PooledObject<Cipher> p) {
        //Destroys an instance no longer needed by the pool. 
        System.out.println("destroying");
    }

    @Override
    public void activateObject(PooledObject<Cipher> p) throws Exception { //Reinitialize an instance to be returned by the pool

        setRunning(true);
    }

    @Override
    public void passivateObject(PooledObject<Cipher> p) throws Exception {   // reset the object after the object returns to the pool

        setRunning(false);
    }

    public void setRunning(boolean running) {

        this.running = running;
    }
//    
}

This is how I implement ObjectPool in my Example class

public Key a(byte[] afyte) throws Exception {

        Cipher cipher = null;
        cipher = pool.borrowObject(); //get the object from the pool
        try {
            System.out.println("****************** After borrow ****************");
            printPool();
            cipher.init(Cipher.DECRYPT_MODE, mkkey, algParamSpec);
            byte[] de = cipher.doFinal(afyte);
            SecretKey mk = new SecretKeySpec(de, "DESede");
            return mk;
        } catch (Exception e) {
            pool.invalidateObject(cipher);
            cipher = null;
        } finally {
            if (null != cipher) {
                pool.returnObject(cipher);
                System.out.println("****************** After return ****************");
                printPool();
            }
        }
        return (Key) cipher;
    }

printPool

public void printPool() {
        System.out.println("Pool for cipher with instances DESede/CBC/NoPadding");
        System.out.println("Active [" + pool.getNumActive() + "]"); //Return the number of instances currently borrowed from this pool
        System.out.println("Idle [" + pool.getNumIdle() + "]"); //The number of instances currently idle in this pool
        System.out.println("Total Created [" + pool.getCreatedCount() + "]");      
    }

Am I on the right path ? Is it possible to increase pool size ?

Edit

The answer from @http works fine to me. But if I have another method encryptECB(Key key, byte[] b), how should I write ?

Any help would be appreciated !

Tony
  • 2,515
  • 14
  • 38
  • 71
  • The pool has a maximum of 10 so if you don't return them to the pool then that error is expected? And if you want to create more if the pool runs outs then it seems like you don't need a pool at all, you can just keep creating them when you need more. – tima May 09 '17 at 04:24
  • Am i on the right path ? – Tony May 09 '17 at 04:31
  • not if you want to keep using a pool. A pool is supposed to have a limited number of resources like in this case you made it 10. That means you can't go past 10, and if they are all busy, you need to wait for one to become available before requesting one. On the other hand, if you don't want to wait that means that you can't have a limit and therefore do not need a pool. – tima May 09 '17 at 04:33
  • @tima I saw this [example](http://www.javadocexamples.com/java_source/net/betterjava/performance/pool/impl/MyObjectPool.java.html) where it can increase the pool size if pool size is empty. But not sure is it possible to do that in my case. – Tony May 09 '17 at 04:42
  • you can try that, I think it's an older version of apache-commons-pool (~2012). You can also try to set your `pool.setMaxTotal(-1);` like this which is supposed to allow as many objects as you need. It doesn't really increase it. – tima May 09 '17 at 04:57
  • @tima No idea on how to implement that as the post was using `private List pool = new LinkedList();` – Tony May 09 '17 at 05:01
  • I wouldn't recommend it because it is old and defeats the purpose. I think you should either increase your initial pool size to suit your needs and add handling for when the pool runs out. Or don't use a pool at all. – tima May 09 '17 at 05:07
  • @tima when should I use the destroyObject method ? – Tony May 09 '17 at 07:53
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/143783/discussion-between-tima-and-tony). – tima May 09 '17 at 12:38
  • This question is too broad, and also is a duplicate of existing questions (already present in the linked section), but the bounty protects the question from being closed. – Oleg Estekhin May 19 '17 at 16:06

1 Answers1

8

You are on the right track. When constructing the GenericObjectPool, you can use the constructor that accepts a GenericObjectPoolConfig object which contains all the configuration values for your object pool. The example below would let your pool grow to 20 connections before it was exhausted...

GenericObjectPoolConfig config = new GenericObjectPoolConfig();
config.setMinIdle(2);
config.setMaxIdle(5);
config.setMaxTotal(20);

GenericObjectPool<Cipher> pool;
CipherFactory factory = new CipherFactory(); 
this.pool = new GenericObjectPool<Cipher>(factory, config);

GenericeObjectPoolConfig also has a setBlockWhenExhausted method to specify the behaviour when the pool has reached the maxTotal connections. See https://commons.apache.org/proper/commons-pool/apidocs/org/apache/commons/pool2/impl/BaseObjectPoolConfig.html#setBlockWhenExhausted-boolean- for details.

A pattern I implement when using commons pool is to create 2 interfaces, one for your pooled object and one for your factory...

public interface PooledCipher extends java.io.Closeable {
    byte[] doFinal(byte[] bytes) throws Exception;
    SecretKeySpec getSecretKeySpec(byte[] bytes) throws Exception;
}

public interface CipherFactory {
    PooledCipher getCipher() throws Exception;        
    void close();
}

CipherFactory implementation...

public class CipherFactoryImpl extends BasePooledObjectFactory<PooledCipher> 
    implements CipherFactory {

    private final GenericObjectPoolConfig config;
    private final GenericObjectPool<PooledCipher> pool;
    private final String transformation;
    private final int opmode;
    private final Key key;
    private final AlgorithmParameters params;
    private final String secretKeySpecAlgorithm;

    public CipherFactoryImpl(GenericObjectPoolConfig config, String transformation, int opmode, Key key, AlgorithmParameters params, String secretKeySpecAlgorithm) {
        this.config = config;
        this.pool = new GenericObjectPool<PooledCipher>(this, config);
        this.transformation = transformation;
        this.opmode = opmode;
        this.key = key;
        this.params = params;       
        this.secretKeySpecAlgorithm = secretKeySpecAlgorithm
    }

    @Override
    public PooledCipher create() throws Exception {
        return new PooledCipherImpl(pool, transformation, opmode, key, params, secretKeySpecAlgorithm);
    }

    @Override
    public PooledCipher getCipher() throws Exception {
        return pool.borrowObject();
    }

    @Override
    public void destroyObject(PooledObject<PooledCipher> p) throws Exception {
        try {
            PooledCipherImpl cipherImpl = (PooledCipherImpl)p.getObject();
            // do whatever you need with cipherImpl to destroy it
        } finally {
            super.destroyObject(p);
        }
    }

    @Override
    public void close() {
        pool.close();
    }

    @Override
    public PooledObject<PooledCipher> wrap(PooledCipher cipher) {
        return new DefaultPooledObject<PooledCipher>(cipher);
    }
}

PooledCipher implementation...

public class PooledCipherImpl implements PooledCipher {
    private final ObjectPool<PooledCipher> pool;
    private final Cipher cipher;
    private final String secretKeySpecAlgorithm;
    private boolean destroyOnClose = false;

    public PooledCipherImpl(ObjectPool<PooledCipher> pool, String transformation, int opmode, Key key, AlgorithmParameters params, String secretKeySpecAlgorithm) {
        this.pool = pool;
        this.cipher = Cipher.getInstance(transformation);
        this.cipher.init(opmode, key, params);
        this.secretKeySpecAlgorithm = secretKeySpecAlgorithm;
    }

    @Override
    public byte[] doFinal(byte[] bytes) throws Exception {
        try {
            return cipher.doFinal(bytes);
        } catch (Exception e) {
           destroyOnClose = true;
           throw e;
        }
    }

    @Override
    public SecretKeySpec getSecretKeySpec(byte[] bytes) {
        return new SecretKeySpec(doFinal(bytes), secretKeySpecAlgorithm);
    }

    @Override
    public void close() throws IOException {
        try {
            if (destroyOnClose) {
                pool.destroyObject(this);
            } else {
                pool.returnObject(this);
            }
        } catch (Exception e) {
            throw new IOException(e);
        }
    }
}

Then you construct your CipherFactory like this...

String transformation = "DESede/CBC/NoPadding";
String secretKeySpecAlgorithm = "DESede";
GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
// set up the poolConfig here
poolConfig.setMaxTotal(20);
CipherFactory cipherFactory = new CipherFactoryImpl(poolConfig, transformation, Cipher.DECRYPT_MODE, mkkey, algParamSpec, secretKeySpecAlgorithm);

And use it like this...

public Key unwrapKey(byte[] tmkByte) throws Exception {
    try (PooledCipher cipher = cipherFactory.getCipher()) {
        return cipher.getSecretKeySpec(tmkByte);
    }
}

Also you can reuse the PooledCipher and CipherFactory interfaces to create other implementations, such as JCA.

httPants
  • 1,832
  • 1
  • 11
  • 13
  • 1
    How does this let the pool grow? It just increases the initial size – tima May 09 '17 at 04:38
  • I noted that there are some example using `GenericObjectPoolConfig`. Why need that since I can use `pool.setMaxTotal(20);` instead of `config.setMaxTotal(20);`. What are the difference actually ? – Tony May 09 '17 at 04:40
  • Let me try first – Tony May 09 '17 at 04:43
  • GenericeObjectPoolConfig also has a setBlockWhenExhausted method to specify the behaviour when the pool has reached the maxTotal connections. See https://commons.apache.org/proper/commons-pool/apidocs/org/apache/commons/pool2/impl/BaseObjectPoolConfig.html#setBlockWhenExhausted-boolean- for details. – httPants May 09 '17 at 04:44
  • I know that, but why we need that since `GenericeObjectPool` already have the behaviour – Tony May 09 '17 at 04:45
  • 1
    I use GenericObjectPoolConfig because I usually create that object and set the values based on a configuration file. In my case I'm using spring, so I can create a GenericObjectPoolConfig bean and initialize all the properties from a properties file. – httPants May 09 '17 at 04:45
  • You can't do that by just using `GenericeObjectPool`? – Tony May 09 '17 at 04:47
  • If the method is available then you can. I just always use GenericObjectPoolConfig as a personal preference since it makes it much easier to pass the configuration into the constructor of my factory implementation class, rather than expose those methods on the implementation class. – httPants May 09 '17 at 04:53
  • 1
    Hi Tony, i've edited my answer to make it much closer to a compilable solution and also to demonstrate how you can destroy objects. Hopefully this is clearer. – httPants May 11 '17 at 01:28