View Javadoc

1   package net.sourceforge.basher.internal.impl;
2   
3   import java.lang.reflect.Method;
4   import java.lang.reflect.Constructor;
5   import java.lang.reflect.Modifier;
6   
7   import net.sourceforge.basher.Task;
8   import net.sourceforge.basher.BasherException;
9   import net.sourceforge.basher.Phase;
10  import net.sourceforge.basher.annotations.*;
11  import net.sourceforge.basher.internal.TaskDecorator;
12  import net.sourceforge.basher.tasks.AbstractTask;
13  import org.ops4j.gaderian.service.*;
14  import org.apache.commons.logging.Log;
15  
16  /**
17   * @author Johan Lindquist
18   * @version $Revision$
19   */
20  public class TaskDecoratorImpl implements TaskDecorator
21  {
22      private ClassFactory _classFactory;
23      private Log _log;
24  
25      public void setLog( final Log log )
26      {
27          _log = log;
28      }
29  
30      public void setClassFactory( final ClassFactory classFactory )
31      {
32          _classFactory = classFactory;
33      }
34  
35      public Task decorateInstance( final Object taskInstance )
36      {
37          try
38          {
39              if (_log.isDebugEnabled())
40              {
41                  _log.debug("Decorating instance: " + taskInstance.getClass().getName());
42              }
43  
44              final String taskInstanceClassName = taskInstance.getClass().getName();
45              final String className = taskInstanceClassName.substring( taskInstanceClassName.lastIndexOf( '.' ) + 1 );
46  
47              final String taskClassName = className + "BasherDecoratedTask";
48  
49              // Determine the execution method name
50              final String executionMethodName = determineExecutionMethodName( taskInstance );
51  
52              // Then create the decorated task
53              return createDecoratedTask( taskClassName, executionMethodName, taskInstance );
54          }
55          catch ( Exception e )
56          {
57              throw new BasherException( e.getMessage(), e );
58          }
59      }
60  
61      private void processMethods( final Class<? extends Object> taskInstanceClass, final ClassFab decoratedTask )
62      {
63          if ( taskInstanceClass.isAssignableFrom( Task.class ) )
64          {
65              // No need to 'enhance' this - already implements the class
66              return;
67          }
68  
69          // By doing depth-first here, we allow sub-classes to override super classes methods
70          final Class superClass = taskInstanceClass.getSuperclass();
71          if ( !superClass.equals( Object.class ) )
72          {
73              processMethods( superClass, decoratedTask );
74          }
75  
76  
77          final Method[] taskMethods = Task.class.getDeclaredMethods();
78          for ( int i = 0; i < taskMethods.length; i++ )
79          {
80              Method taskMethod = taskMethods[ i ];
81  
82              try
83              {
84                  final Method declaredMethod = taskInstanceClass.getDeclaredMethod( taskMethod.getName(), taskMethod.getParameterTypes() );
85  
86                  final MethodSignature methodSignature = new MethodSignature( declaredMethod );
87  
88                  final MethodFab methodFab = decoratedTask.getMethodFab( methodSignature );
89  
90                  // Only if we haven't enhanced this method already do we do something
91                  if ( methodFab == null )
92                  {
93                      // If we get here, it means we have the method available in the task
94                      // If so, add method, proxying the call to the instance
95                      decoratedTask.addMethod( Modifier.PUBLIC, methodSignature, "return _taskInstance." + declaredMethod.getName() + "();" );
96                  }
97  
98              }
99              catch ( NoSuchMethodException e )
100             {
101                 // Safe to ignore - class may not actually declare it
102             }
103 
104         }
105 
106     }
107 
108     private Task createDecoratedTask( final String decoratedTaskClassName, final String executionMethodName, final Object taskInstance ) throws Exception
109     {
110         final ClassFab fab = _classFactory.newClass( decoratedTaskClassName, DecoratedTask.class );
111         fab.addField( "_taskInstance", taskInstance.getClass() );
112 
113         final BodyBuilder bodyBuilder = new BodyBuilder();
114         bodyBuilder.begin();
115         bodyBuilder.addln( "_taskInstance." + executionMethodName + "();" );
116         bodyBuilder.end();
117 
118         fab.addMethod( java.lang.reflect.Modifier.PUBLIC, new MethodSignature( void.class, "doExecuteTask", new Class[0], new Class[]{ Throwable.class } ), bodyBuilder.toString() );
119 
120         bodyBuilder.clear();
121 
122         // Now, add override for all 'Task' methods
123         processMethods( taskInstance.getClass(), fab );
124 
125         bodyBuilder.begin();
126         bodyBuilder.addln( "super();" );
127         bodyBuilder.addln( "_taskInstance = $1;" );
128         bodyBuilder.end();
129 
130         fab.addConstructor( new Class[]{ taskInstance.getClass() }, new Class[0], bodyBuilder.toString() );
131 
132         final Class<Task> decoratedTask = fab.createClass();
133 
134         final Constructor<Task> constructor = decoratedTask.getConstructor( taskInstance.getClass() );
135 
136         return constructor.newInstance( taskInstance );
137 
138     }
139 
140     String determineExecutionMethodName( final Object taskInstance )
141     {
142         final Method[] methods = taskInstance.getClass().getMethods();
143 
144         Method selectedMethod = null;
145 
146         for ( final Method method : methods )
147         {
148             if ( method.getAnnotation( BasherExecuteMethod.class ) != null )
149             {
150                 if ( selectedMethod == null )
151                 {
152                     // Found an execution method
153                     selectedMethod = method;
154                 }
155                 else
156                 {
157                     throw new BasherException( "Found more than 1 execute method", null );
158                 }
159             }
160         }
161 
162         if ( selectedMethod != null )
163         {
164             validateExecutionMethod( selectedMethod );
165             return selectedMethod.getName();
166         }
167 
168         // Ok, look for the default method.
169         try
170         {
171             taskInstance.getClass().getMethod( "executeTask" );
172             return "executeTask";
173         }
174         catch ( NoSuchMethodException e )
175         {
176             throw new BasherException( "Could not find executeTask", e );
177         }
178 
179     }
180 
181     private void validateExecutionMethod( Method method )
182     {
183         if ( method.getParameterTypes().length != 0 )
184         {
185             throw new BasherException( "Execution method '" + method.getName() + "'  must not take any parameters", null );
186         }
187 
188     }
189 }