commons pool を AOP で処理する

Apache commons pool の利用を、Google guice AOP 使用 で書いてみました。
仕様で重要な点は、
・POOL を定義して参照する為の Key をメソッドアノテーション
 POOL から取得Object が return 値になるようにする
・このメソッドアノテーションを付けるメソッドはメソッド内に処理を書いても、
 無意味でreturn 値だけが意味を持つのでインターフェースの default メソッドとして用意するのを慣習とする。
・POOL インスタンスはシングルトンで持ちメソッドアノテーションで指定する value() をキーとして管理する。
・AOPインターセプタは提供されるものを使用する。

package poolaop の中の、class, interface , annotation

import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.*;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import com.google.inject.BindingAnnotation;
/**
 * Pool
 */
@Retention(RUNTIME)
@Target(METHOD)
@BindingAnnotation
public @interface Pool{
    String value();
}
import java.io.Serializable;
import java.lang.annotation.Annotation;
import com.google.inject.internal.Annotations;
/**
 * PoolImpl : Poolアノテーションを特定する為に使用
 */
final class PoolImpl implements Pool, Serializable{
   private final String value;

   protected PoolImpl(String value){
      this.value = value;
   }
   @Override
   public String value(){
      return this.value;
   }
   @Override
   public Class<? extends Annotation> annotationType(){
      return Pool.class;
   }
   @Override
   public int hashCode(){
      return (127 * "value".hashCode()) ^ value.hashCode();
   }
   @Override
   public boolean equals(Object o){
      if (!(o instanceof Pool)){
         return false;
      }
      Pool other = (Pool)o;
      return value.equals(other.value());
   }
   @Override
   public String toString(){
      return "@" + Pool.class.getName() + "(value=" + Annotations.memberValueString(value) + ")";
   }
   private static final long serialVersionUID = 0;
}

POOL 管理クラス

import java.util.HashMap;
import java.util.Map;
import org.apache.commons.pool2.BasePooledObjectFactory;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
/**
 * PoolInstant
 */
public final class PoolInstant<T>{
   private static PoolInstant inst;

   private Map<String, GenericObjectPool<T>> poolMap;

   private PoolInstant(){
      poolMap = new HashMap<>();
   }

   public static synchronized PoolInstant getInstance(){
      if (inst==null) inst = new PoolInstant<>();
      return inst;
   }
   public void setting(String key, BasePooledObjectFactory factory){
      setting(key, 1, 1, factory);
   }
   public void setting(String key, int poolcount, BasePooledObjectFactory factory){
      setting(key, poolcount, poolcount, factory);
   }
   @SuppressWarnings({ "resource", "unchecked" })
   public void setting(String key, int poolcount, int maxcount, BasePooledObjectFactory factory){
      if (poolMap.containsKey(key)){
         throw new IllegalArgumentException("Pool setting is One time Only!");
      }
      try{
         GenericObjectPoolConfig<T> config = new GenericObjectPoolConfig<T>();
         config.setMaxIdle(poolcount);
         config.setMaxTotal(maxcount);
         poolMap.put(key, new GenericObjectPool<T>(factory, config));
         GenericObjectPool<T> pool = poolMap.get(key);
         for(int i=0; i < poolcount; i++){
            pool.addObject();
         }
      }catch(Exception ex){
         throw new RuntimeException(ex);
      }
   }

   protected T borrow(String key) throws Exception{
      return poolMap.get(key).borrowObject();
   }
   protected void returnObject(String key, T t){
      poolMap.get(key).returnObject(t);
   }

   public int getNumIdle(String key){
      return poolMap.get(key).getNumIdle();
   }
   public long getNumActive(String key){
      return poolMap.get(key).getNumActive();
   }
   public int getMaxIdle(String key){
      return poolMap.get(key).getMaxIdle();
   }
   public long getMaxTotal(String key){
      return poolMap.get(key).getMaxTotal();
   }
   public long getBorrowedCount(String key){
      return poolMap.get(key).getBorrowedCount();
   }
   public long getReturnedCount(String key){
      return poolMap.get(key).getReturnedCount();
   }
   public long getDestroyedCount(String key){
      return poolMap.get(key).getDestroyedCount();
   }
   public void shutdown(){
      poolMap.entrySet().stream().forEach(e->e.getValue().close());
   }
}

POOL から Object を取得するインターセプタ、通常インターセプタは、MethodInvocation の proceed() を
実行して戻り値を返すようにするが、敢えて実行しないで、POOL から取得した Object を返す。

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
/**
 * PoolInterceptor
 */
final class PoolInterceptor implements MethodInterceptor{
   private String key;

   protected PoolInterceptor(String key){
      this.key = key;
   }
   @SuppressWarnings("unchecked")
   @Override
   public Object invoke(MethodInvocation m) throws Throwable{
      PoolInstant instant = PoolInstant.getInstance();
      Object o = instant.borrow(key);
      instant.returnObject(key, o);
      return o;
   }
}

上のインターセプタをバインド定義する Google guice の Module

import java.util.Arrays;
import com.google.inject.AbstractModule;
import com.google.inject.matcher.Matchers;
/**
 * PoolModule
 */
public class PoolModule extends AbstractModule{
   private String[] poolkeys;

   public PoolModule(String...poolkeys){
      if (poolkeys.length < 1) throw new IllegalArgumentException("must be poolkeys length > 0");
      this.poolkeys = poolkeys;
   }
   @Override
   protected void configure(){
      Arrays.asList(poolkeys).stream().forEach(k->{
         binder().bindInterceptor(Matchers.any(), Matchers.annotatedWith(Pools.keyed(k)), new PoolInterceptor(k));
      });
   }
}

上で呼び出す Pool アノテーション特定処理

/**
 * Pools
 */
public final class Pools{
   protected static Pool keyed(String key){
      return new PoolImpl(key);
   }
}

ここまでが package で提供するもので、@Pool , PoolModule, PoolInstant を使用することになる。
利用側として、、@Pool  を以下のように、interface を用意する。この default メソッドで処理を書いても無意味で
戻り値のみ有効だ。
この interface は、呼出し設計する側が用意する。

public interface PoolGetter<T>{
    @Pool("A")
    default T getPoolObject(){
        return null;
    }
}

サンプル
POOLから、IdMaker という Object インスタンスを取得して処理するもの。。

public class TestLogicImpl implements TestLogic, PoolGetter<IdMaker>{
   @Override
   public String getId(){
      IdMaker idMaker = getPoolObject();
      return idMaker.getId();
   }
}

Google guice AOPのバインド定義させる処理と実行、@Pool("A") を設計したキーで実行

Injector injector = Guice.createInjector(new PoolModule(”A"));
TestLogic logic = injector.getInstance(TestLogicImpl.class);
String id = logic.getId();

この中で、通常コードとしてクレイジーなのは、アノテーションimplements したクラスを用意することで
普通こんなものをコーディングしない。