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 したクラスを用意することで
普通こんなものをコーディングしない。