/******************************************************************************* * Copyright (c) 2013 Rene Schneider, GEBIT Solutions GmbH 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 *******************************************************************************/ package de.gebit.integrity.operations.custom; import java.lang.reflect.Method; import com.google.inject.Binder; import com.google.inject.Inject; import com.google.inject.Injector; import com.google.inject.Module; import de.gebit.integrity.classloading.IntegrityClassLoader; import de.gebit.integrity.dsl.CustomOperation; import de.gebit.integrity.dsl.OperationDefinition; import de.gebit.integrity.exceptions.ModelRuntimeLinkException; import de.gebit.integrity.modelsource.ModelSourceExplorer; import de.gebit.integrity.operations.UnexecutableException; import de.gebit.integrity.parameter.conversion.ValueConverter; /** * The custom operation wrapper is used to wrap a custom operation class and instance for execution. The wrapper does * perform class loading and instantiation. * * @author Rene Schneider - initial API and implementation * */ public class CustomOperationWrapper { /** * The operation to perform. */ private CustomOperation operation; /** * The operation class. */ private Class<? extends de.gebit.integrity.operations.custom.Operation<?, ?, ?>> operationClass; /** * The value converter to use. */ @Inject private ValueConverter valueConverter; /** * The Guice injector. */ @Inject private Injector injector; /** * The classloader. */ @Inject private IntegrityClassLoader classLoader; /** * The model source explorer. */ @Inject private ModelSourceExplorer modelSourceExplorer; /** * Creates a new wrapper instance. This also loads the actual operation implementation class using the injected * classloader. * * @param anOperation * the operation to wrap * @param anInjector * the Guice injector (can't wait to inject it because we require stuff injected during construction) * @throws ClassNotFoundException * if the operations' class could not be found */ @SuppressWarnings("unchecked") public CustomOperationWrapper(CustomOperation anOperation, Injector anInjector) throws ClassNotFoundException { anInjector.injectMembers(this); operation = anOperation; OperationDefinition tempDefinition = operation.getDefinition(); if (tempDefinition == null) { throw new ModelRuntimeLinkException("No definition found for operation.", operation, modelSourceExplorer.determineSourceInformation(operation)); } operationClass = (Class<? extends de.gebit.integrity.operations.custom.Operation<?, ?, ?>>) classLoader .loadClass(tempDefinition); } /** * Executes the wrapped operation logic. * * @return the result of the operation * @throws UnexecutableException * if the operation cannot be executed for any reason */ @SuppressWarnings({ "unchecked", "rawtypes" }) public Object executeOperation() throws UnexecutableException { de.gebit.integrity.operations.custom.Operation tempOperationInstance; try { tempOperationInstance = operationClass.newInstance(); } catch (IllegalAccessException exc) { throw new UnexecutableException(exc); } catch (InstantiationException exc) { throw new UnexecutableException(exc); } injector.createChildInjector(new Module() { @Override public void configure(Binder aBinder) { aBinder.bind(CustomOperation.class).toInstance(operation); } }).injectMembers(tempOperationInstance); Object tempConvertedPrefixParameter = null; if (operation.getPrefixOperand() != null) { tempConvertedPrefixParameter = valueConverter.convertValue(determinePrefixParameterTargetType(), operation.getPrefixOperand(), null); } Object tempConvertedPostfixParameter = null; if (operation.getPostfixOperand() != null) { tempConvertedPostfixParameter = valueConverter.convertValue(determinePostfixParameterTargetType(), operation.getPostfixOperand(), null); } return tempOperationInstance.execute(tempConvertedPrefixParameter, tempConvertedPostfixParameter); } /** * Determines the type of the operation prefix parameter. * * @return the expected type */ protected Class<?> determinePrefixParameterTargetType() { // first parameter is the prefix parameter return determineExecuteMethod().getParameterTypes()[0]; } /** * Determines the type of the operation postfix parameter. * * @return the expected type */ protected Class<?> determinePostfixParameterTargetType() { // second parameter is the postfix parameter return determineExecuteMethod().getParameterTypes()[1]; } /** * Searches the operation class for the "execute" method, and returns the corresponding reflection method instance. * * @return the method representation */ protected Method determineExecuteMethod() { Method tempMethod = null; for (Method tempPossibleMethod : operationClass.getMethods()) { if (!tempPossibleMethod.isBridge() && "execute".equals(tempPossibleMethod.getName())) { tempMethod = tempPossibleMethod; break; } } if (tempMethod == null) { throw new IllegalStateException("Found no execute method on operation class " + operationClass + "."); } return tempMethod; } }