View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.pipeline.stage;
19  
20  import java.lang.reflect.InvocationTargetException;
21  import java.lang.reflect.Method;
22  
23  import org.apache.commons.pipeline.StageException;
24  
25  /**
26   * <p>
27   * Provide this Stage with a class and a static method name and it will
28   * dynamically look up the appropriate method to call based on the object type.
29   * If the object type is an array, it will assume that the method that needs to
30   * be called contains the method signature as described by the objects in the
31   * array. The object returned from the method call will be exqueued.
32   * </p>
33   *
34   * <p>
35   * The resulting object will be exqueued on the main pipeline if it is not null.
36   * If it is null, we will try to place the original object on the branch
37   * specified by the nullResultBranchKey property. The default for this value is
38   * "nullResult".
39   * </p>
40   */
41  public class DynamicLookupStaticMethodStage extends BaseStage {
42      
43      // Branch upon which the original objects will be enqueued if the defined
44      // Method returned a null result.
45      private String nullResultBranchKey;
46      
47      // Name of the method to call to process the object.
48      private String methodName;
49      
50      // Class containing the method.
51      private Class clazz;
52      
53      /**
54       * Creates a new instance of DynamicLookupStaticMethodStage
55       *
56       * @param clazz
57       *            The class that defines the static method that will be used to
58       *            process objects.
59       * @param methodName
60       *            The name of the method. This method may be overloaded.
61       */
62      public DynamicLookupStaticMethodStage(Class clazz, String methodName) {
63          if (clazz == null) throw new IllegalArgumentException("Argument 'clazz' can not be null.");
64          if (methodName == null) throw new IllegalArgumentException("Argument 'methodName' can not be null.");
65          
66          this.clazz = clazz;
67          this.methodName = methodName;
68      }
69      
70      /**
71       * Creates a new DynamicLookupStaticMethodStage for the specified class and
72       * static method.
73       *
74       * @param className
75       *            The fully qualified class name of the class in which the
76       *            static method that will be used to process objects is defined.
77       * @param methodName
78       *            The name of the method. This method may be overloaded.
79       * @throws ClassNotFoundException
80       *             if the specified class cannot be loaded.
81       */
82      public DynamicLookupStaticMethodStage(String className, String methodName) throws ClassNotFoundException {
83          this(Thread.currentThread().getContextClassLoader().loadClass(className), methodName);
84      }
85      
86      /**
87       * <p>
88       * Finds the appropriate method overloading for the method specified by
89       * {@link #getMethodName() methodName}, calls it to process the object, and
90       * exqueues any returned object. If the returned object is null, the
91       * original object is enqueued on the branch specified by the
92       * nullResultBranchKey property.
93       * </p>
94       *
95       * @param obj
96       *            The object to process.
97       */
98      public void process(Object obj) throws StageException {
99          Class[] argTypes;
100         if (obj.getClass().isArray()) {
101             Object[] objs = (Object[]) obj;
102             argTypes = new Class[objs.length];
103             for (int i = 0; i < objs.length; i++) {
104                 argTypes[i] = objs[i].getClass();
105             }
106         } else {
107             argTypes = new Class[] {obj.getClass()};
108         }
109         
110         try {
111             Method method = this.clazz.getMethod(methodName, argTypes);
112             
113             // due to the way that varargs work, we need to ensure that we get
114             // the correct compile-time overloading of method.invoke()
115             Object result = obj.getClass().isArray() ? method.invoke(null, (Object[]) obj) : method.invoke(null, obj);
116             if (result != null){
117                 this.emit(result);
118             } else if (this.nullResultBranchKey != null) {
119                 this.context.getBranchFeeder(this.nullResultBranchKey).feed(obj);
120             }
121         } catch (NoSuchMethodException e){
122             StringBuilder message = new StringBuilder("No acceptable method " + methodName + " found with argument(s) of type: [ ");
123             for (Class<?> clazz : argTypes) message.append(clazz.getName()).append(" ");
124             message.append("]");
125             
126             throw new StageException(this, message.toString() ,e);
127         } catch (IllegalAccessException e){
128             throw new StageException(this, e);
129         } catch (InvocationTargetException e){
130             throw new StageException(this, e);
131         }
132     }
133     
134     /** Returns the name of the method to be executed. */
135     public String getMethodName(){
136         return this.methodName;
137     }
138     
139     /** Returns the class containing the method to be executed */
140     public Class getMethodClass(){
141         return this.clazz;
142     }
143     
144     /**
145      * Getter for property nullResultBranchKey.
146      *
147      * @return Value of property nullResultBranchKey.
148      */
149     public String getNullResultBranchKey() {
150         return this.nullResultBranchKey;
151     }
152     
153     /**
154      * Setter for property nullResultBranchKey.
155      *
156      * @param nullResultBranchKey
157      *            New value of property nullResultBranchKey.
158      */
159     public void setNullResultBranchKey(String nullResultBranchKey) {
160         this.nullResultBranchKey = nullResultBranchKey;
161     }
162     
163 }