TransactionListDispatcherMixin.java
/***************************************************************************
Copyright 2014 Emily Estes
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
***************************************************************************/
package net.metanotion.util;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.sql.DataSource;
import net.metanotion.functor.Block;
/** This dispatcher uses reflection to look for parameters of type {@link java.sql.Connection} and if it finds one, it
looks at the Unknown implementation during message evaluation to look up a DataSource instance and wraps the method
call in a SQL Transaction.
@param <O> The type of the class the child dispatcher's messages are evaluated against.
*/
public final class TransactionListDispatcherMixin<O> implements Dispatcher<Unknown, Map.Entry<String,Iterable>> {
private final Class<O> klazz;
private final Dispatcher<Unknown,Map.Entry<String,Iterable>> disp;
private final Map<String,Iterable<ParamGetter>> methodMap = new HashMap<>();
/** Create a new service list dispatcher.
@param klazz the class to find parameters marked with {@literal @}Service annotations and construct a
dispatcher against.
@param disp The dispatcher to delegate the final parameter list with.
*/
public TransactionListDispatcherMixin(final Class<O> klazz, final Dispatcher<Unknown,Map.Entry<String,Iterable>> disp) {
this.klazz = klazz;
this.disp = disp;
for(final Method m: klazz.getMethods()) {
final ArrayList<ParamGetter> getters = new ArrayList<>();
final Class[] pTypes = m.getParameterTypes();
int connCounter = 0;
for(int i=0; i < pTypes.length; i++) {
ParamGetter g = TransactionListDispatcherMixin.NP_INSTANCE;
if(Connection.class.isAssignableFrom(pTypes[i])) {
g = TransactionListDispatcherMixin.CP_INSTANCE;
connCounter++;
}
getters.add(g);
}
if(connCounter > 0) { methodMap.put(m.getName(), getters); }
}
}
private interface ParamGetter {
public Object getParam(Unknown u, Iterator<Object> params, Connection conn);
}
private static final ParamGetter NP_INSTANCE = new ParamGetter() {
@Override public Object getParam(final Unknown u, final Iterator<Object> params, final Connection conn) {
return params.next();
}
};
private static final ParamGetter CP_INSTANCE = new ParamGetter() {
@Override public Object getParam(final Unknown u, final Iterator<Object> params, final Connection conn) {
params.next();
return conn;
}
};
private final class Msg implements Message<Unknown> {
private final Map.Entry<String,Iterable> data;
private final Iterable<ParamGetter> getters;
public Msg(final Map.Entry<String,Iterable> data, final Iterable<ParamGetter> getters) {
this.data = data;
this.getters = getters;
}
@Override public Object call(final Unknown u) {
return JDBCTransaction.doTX(u.lookupInterface(DataSource.class), new Block<Connection,Object>() {
public Object eval(final Connection conn) throws Exception {
final ArrayList<Object> params = new ArrayList<>();
final Iterator it = data.getValue().iterator();
for(final ParamGetter g: getters) { params.add(g.getParam(u, it, conn)); }
final Message m = disp.dispatch(new Pair<String,Iterable>(data.getKey(), params));
return m.call(u.lookupInterface(m.receiverType()));
}
});
}
@Override public Class<Unknown> receiverType() { return Unknown.class; }
}
@Override public Message<Unknown> dispatch(final Map.Entry<String,Iterable> data) {
final Iterable<ParamGetter> m = methodMap.get(data.getKey());
if(m == null) { return disp.dispatch(data); }
return new Msg(data, m);
}
}