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 }