/* * Copyright (c) 2006-2011 Nuxeo SA (http://nuxeo.com/) and others. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Florent Guillaume */ package org.eclipse.ecr.core.storage.sql.net; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.SynchronousQueue; import org.eclipse.ecr.core.api.WrappedException; import org.eclipse.ecr.core.storage.StorageException; import org.eclipse.ecr.core.storage.sql.CachingRowMapper; import org.eclipse.ecr.core.storage.sql.InvalidationsQueue; import org.eclipse.ecr.core.storage.sql.Mapper; import org.eclipse.ecr.core.storage.sql.Repository; import org.eclipse.ecr.core.storage.sql.Session; /** * Will execute in a separate thread the commands passed through {@link #call}. */ public class MapperInvoker extends Thread { private static final String INVOKER_INIT = "__init"; private static final String INVOKER_CLOSE = "__close"; private static final Map<String, Method> mapperMethods = new HashMap<String, Method>(); static { for (Method m : Mapper.class.getMethods()) { mapperMethods.put(m.getName(), m); } } protected static final class MethodCall { public final String methodName; public final Object[] args; protected final CountDownLatch resultReady; protected Object result; public MethodCall(String methodName, Object[] args) { this.methodName = methodName; this.args = args; this.resultReady = new CountDownLatch(1); } public void setResult(Object result) { this.result = result; resultReady.countDown(); } public Object getResult() throws InterruptedException { resultReady.await(); return result; } } private final Repository repository; private final InvalidationsQueue eventQueue; private Session session; private Mapper mapper; protected MapperClientInfo clientInfo; protected final SynchronousQueue<MethodCall> methodCalls; public MapperInvoker(Repository repository, String name, InvalidationsQueue eventQueue, MapperClientInfo info) throws Throwable { super(name); this.repository = repository; this.eventQueue = eventQueue; this.methodCalls = new SynchronousQueue<MethodCall>(); this.clientInfo = info; start(); init(); } // called in the main thread public void init() throws Throwable { call(INVOKER_INIT); } // called in the main thread public void close() throws Throwable { try { call(INVOKER_CLOSE); } finally { interrupt(); join(); } } // called in the main thread public Object call(String methodName, Object... args) throws Throwable { MethodCall call = new MethodCall(methodName, args); methodCalls.put(call); return call.getResult(); } @Override public void run() { try { while (true) { MethodCall call = methodCalls.take(); Object res; try { res = localCall(call.methodName, call.args); } catch (InvocationTargetException e) { res = WrappedException.wrap(e.getCause()); } catch (Exception e) { // wrap the exception as its class may not be present on the // server, or be slightly different // (javax.resource.ResourceException) res = WrappedException.wrap(e); } call.setResult(res); } } catch (InterruptedException e) { // end } } protected Object localCall(String methodName, Object[] args) throws Exception { if (methodName == INVOKER_INIT) { // == is ok session = repository.getConnection(); mapper = session.getMapper(); // replace event queue with the client-repo specific one ((CachingRowMapper) mapper).setEventQueue(eventQueue); return null; } else if (methodName == INVOKER_CLOSE) { // == is ok session.close(); mapper = null; return null; } else if (Mapper.CLOSE.equals(methodName)) { // ignored, done by above invoker close, on the session // (we must not close the mapper directly as it may be in a pool) return null; } Method method = mapperMethods.get(methodName); if (method == null) { throw new StorageException("Unknown Mapper method: " + methodName); } else { return method.invoke(mapper, args); } } }