/**
* Copyright 2011 meltmedia
*
* 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 org.xchain.framework.lifecycle;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import javax.xml.namespace.QName;
/**
* The implementation of ThreadStep for annotated methods.
*
* @author Christian Trimble
*/
class AnnotationThreadStep
implements ThreadStep
{
/** The method to be called before a thread accesses XChains. */
private Method startMethod;
/** The method to be called after a thread acceses XChains. */
private Method stopMethod;
/**
* The static method used to get an instance to call the start and stop methods on. This is required if the start method,
* or the stop method is an instance level method.
*/
private Method lifecycleAccessor;
/** The unique name of this step. */
private QName qName;
/**
* Created a new AnnotationThreadStep.
*/
AnnotationThreadStep()
{
}
/**
* Creates a new AnnotationThreadStep with the specified name.
*/
AnnotationThreadStep(QName qName) {
this();
this.setQName(qName);
}
/**
* Sets the unique name for this step.
*/
public void setQName(QName qName) { this.qName = qName; }
/**
* Returns the unique name for this step.
*/
public QName getQName() { return this.qName; }
/**
* Sets the method that is called when a thread is started.
*/
public void setStartMethod( Method startMethod ) { this.startMethod = startMethod; }
/**
* Returns the method that is called when a thread is started.
*/
public Method getStartMethod() { return this.startMethod; }
/**
* Sets the method that is called when a thread is stopped.
*/
public void setStopMethod( Method stopMethod ) { this.stopMethod = stopMethod; }
/**
* Returns the method that is called when a thread is stopped.
*/
public Method getStopMethod() { return this.stopMethod; }
/**
* Sets the static method that is used to find the target of non-static start and stop methods.
*/
public void setLifecycleAccessor( Method lifecycleAccessor ) { this.lifecycleAccessor = lifecycleAccessor; }
/**
* Returns the static method that is used to find the target of non-static start and stop methods.
*/
public Method getLifecycleAccessor() { return this.lifecycleAccessor; }
/**
* Calls the start method for this step, if it is defined.
*/
public void startThread(ThreadContext context)
throws LifecycleException
{
if( startMethod != null ) {
try {
invokeStepMethod(startMethod, lifecycleAccessor, context);
}
catch( InvocationTargetException ite ) {
if( ite.getCause() instanceof LifecycleException ) {
throw (LifecycleException)ite.getCause();
}
else if( ite.getCause() instanceof RuntimeException ) {
throw (RuntimeException)ite.getCause();
}
else if( ite.getCause() instanceof Error ) {
throw (Error)ite.getCause();
}
else {
throw new LifecycleException("A start method threw an unknown exception type.", ite);
}
}
catch( Exception e ) {
throw new LifecycleException("Could not execute lifecycle start step method.", e);
}
}
}
/**
* Calls the stop method for this step, if it is defined.
*/
public void stopThread(ThreadContext context)
{
try {
if( stopMethod != null ) {
invokeStepMethod(stopMethod, lifecycleAccessor, context);
}
}
catch( InvocationTargetException ite ) {
if( ite.getCause() instanceof RuntimeException ) {
throw (RuntimeException)ite.getCause();
}
else if( ite.getCause() instanceof Error ) {
throw (Error)ite.getCause();
}
else {
throw new RuntimeException("A start thread method threw an unknown exception type.", ite);
}
}
catch( RuntimeException re ) {
throw re;
}
catch( Exception e ) {
throw new RuntimeException("A thread stop step threw an exception.", e);
}
}
/**
* Invokes a start or stop method for the specified thread context.
*/
private static Object invokeStepMethod(Method stepMethod, Method lifecycleAccessor, ThreadContext threadContext)
throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, LifecycleException
{
// decide what object we will be invoking the method on.
Object lifecycleObject = null;
if( !Modifier.isStatic(stepMethod.getModifiers()) ) {
if( lifecycleAccessor == null ) {
throw new LifecycleException(
"Cannot execute step method '"+stepMethod.getName()+"' of class '"+stepMethod.getDeclaringClass().getName()+"' without an accessor method.");
}
lifecycleObject = lifecycleAccessor.invoke(null);
}
// create the parameters that we will pass to the object.
Class<?>[] paramTypes = stepMethod.getParameterTypes();
Object[] values = new Object[paramTypes.length];
for( int i = 0; i < paramTypes.length; i++ ) {
if( paramTypes[i].isInstance(threadContext) ) {
values[i] = threadContext;
}
}
// invoke the method and return it's value.
return stepMethod.invoke(lifecycleObject, values);
}
}