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 }