package org.agnitas.emm.extension;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import org.agnitas.emm.extension.annotation.DispatchTarget;
import org.agnitas.emm.extension.exceptions.ExtensionException;
import org.agnitas.emm.extension.util.ExtensionUtils;
import org.apache.log4j.Logger;
import org.java.plugin.registry.Extension;
import org.springframework.context.ApplicationContext;
/**
* High sophisticated base class for plugins using annotations.
*
* When the plugin extending this class is invoked, the request parameter "method" is read and depending of the
* annotations, a method is chosed to be invoked.
*
* The decision is done by these properties:
* <ol>
* <li>The method must be annotated with <i>@DispatchTarget</i></li>
* <li>2. The annotated method must not be <i>invoke</i>, <i>setup</i>, <i>unknownTarget</i> or <i>unspecifiedTarget</i></li>
* <li>3. The method must match this signature:
* <ul>
* <li>Return type is <i>void</i></li>
* <li>First argument type is <i>org.agnitas.emm.extension.PluginContext</i></li>
* <li>Second argument type is <i>org.java.plugin.registry.Extension</i></li>
* <li>Third argument type is <i>org.springframework.context.ApplicationContext</i></li>
* </ul></li>
* <li>The value of "name" attribute of the DispatchTarget annotation must match the value "method" request parameter.</li>
* </ol>
*
* If the request parameter "method" is not present, the method <i>unspecifiedTarget</i> is invoked.
* When there is no matching annotation, the method <i>unknownTarget</i> is invoked.
*
* @author md
*
*/
public class AnnotatedDispatchingEmmFeatureExtension implements EmmFeatureExtension {
private static final transient Logger logger = Logger.getLogger( AnnotatedDispatchingEmmFeatureExtension.class);
private final Map<String, Method> dispatchTargetMap;
public AnnotatedDispatchingEmmFeatureExtension() {
this.dispatchTargetMap = collectDispatchTargets();
}
private Map<String, Method> collectDispatchTargets() {
Map<String, Method> map = new HashMap<String, Method>();
Method[] methods = this.getClass().getMethods();
if( logger.isInfoEnabled())
logger.debug( "Searching dispatch targets in class " + this.getClass().getCanonicalName());
for( Method method : methods) {
DispatchTarget annotation = method.getAnnotation( DispatchTarget.class);
if( annotation != null) {
if( isRestrictedName( method.getName())) {
throw new RuntimeException( "Cannot use method " + method.getName() + " as dispatch target");
}
if( annotation.name() == null || annotation.name().equals( "")) {
throw new RuntimeException( "Attribute name not set for DispatchTarget for method " + this.getClass().getCanonicalName() + "." + method.getName());
}
if( !isValidSignature( method)) {
throw new RuntimeException( "Signature of method " + method.getName() + " does not match dispatch target specification");
}
if( map.containsKey( annotation.name())) {
throw new RuntimeException( "Duplicate target name: " + annotation.name());
}
map.put( annotation.name(), method);
}
}
return map;
}
private boolean isRestrictedName( String name) {
return name.equals( "invoke") || name.equals( "setup") || name.equals( "unspecifiedTarget") || name.equals( "unknownTarget");
}
private boolean isValidSignature( Method method) {
Class<?> expectedSignature[] = new Class<?>[] { PluginContext.class, Extension.class, ApplicationContext.class };
// Check return type
if( !method.getReturnType().equals( Void.TYPE))
return false;
// Check method parameters
Class<?> parameterTypes[] = method.getParameterTypes();
if( parameterTypes.length != expectedSignature.length)
return false;
for( int i = 0; i < parameterTypes.length; i++) {
if( !parameterTypes[i].equals( expectedSignature[i]))
return false;
}
return true;
}
@Override
public void invoke( PluginContext pluginContext, Extension extension, ApplicationContext context) throws ExtensionException {
String targetName = ExtensionUtils.getDispatchParameterValue( pluginContext);
if( logger.isDebugEnabled())
logger.debug( "Dispatch target to invoke: " + targetName);
if( targetName == null) {
try {
unspecifiedTarget( pluginContext, extension, context);
} catch( Throwable t) {
throw new ExtensionException( t);
}
} else {
Method method = dispatchTargetMap.get( targetName);
if( method == null) {
try {
unknownTarget( pluginContext, extension, context, targetName);
} catch( Throwable t) {
throw new ExtensionException( t);
}
} else {
try {
method.invoke( this, pluginContext, extension, context);
} catch( InvocationTargetException e) {
logger.error( "Error calling method", e);
} catch( IllegalArgumentException e) {
logger.error( "Illegal argument calling method", e);
} catch( IllegalAccessException e) {
logger.error( "Access error calling method", e);
}
}
}
}
@Override
public void setup( PluginContext pluginContext, Extension extension, ApplicationContext context) throws ExtensionException {
// TODO Auto-generated method stub
}
/**
* Method to be called, when the request parameter "method" is not present.
*
* @param pluginContext the plugin context
* @param extension the extension data
* @param context the Spring application context
*
* @throws Throwable
*/
public void unspecifiedTarget( PluginContext pluginContext, Extension extension, ApplicationContext context) throws Throwable {
// Does nothing
}
/**
* Method to be called, when the value request parameter "method" does not match any annotation.
*
* @param pluginContext the plugin context
* @param extension the extension data
* @param context the Spring application context
* @param targetName of the dispatch target
*
* @throws Throwable
*/
public void unknownTarget( PluginContext pluginContext, Extension extension, ApplicationContext context, String targetName) throws Throwable {
// Does nothing
}
}