001/** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.camel.processor.aggregate; 018 019import java.lang.reflect.Method; 020import java.lang.reflect.Modifier; 021import java.lang.reflect.Proxy; 022import java.util.ArrayList; 023import java.util.Arrays; 024import java.util.List; 025 026import org.apache.camel.CamelContext; 027import org.apache.camel.CamelContextAware; 028import org.apache.camel.Exchange; 029import org.apache.camel.support.ServiceSupport; 030import org.apache.camel.util.ServiceHelper; 031 032/** 033 * An {@link AggregationStrategy} that adapts to a POJO. 034 * <p/> 035 * This allows end users to use POJOs for the aggregation logic, instead of having to implement the 036 * Camel API {@link AggregationStrategy}. 037 */ 038public final class AggregationStrategyBeanAdapter extends ServiceSupport implements AggregationStrategy, CamelContextAware { 039 040 private static final List<Method> EXCLUDED_METHODS = new ArrayList<Method>(); 041 private CamelContext camelContext; 042 private Object pojo; 043 private final Class<?> type; 044 private String methodName; 045 private boolean allowNullOldExchange; 046 private boolean allowNullNewExchange; 047 private volatile AggregationStrategyMethodInfo mi; 048 049 static { 050 // exclude all java.lang.Object methods as we dont want to invoke them 051 EXCLUDED_METHODS.addAll(Arrays.asList(Object.class.getMethods())); 052 // exclude all java.lang.reflect.Proxy methods as we dont want to invoke them 053 EXCLUDED_METHODS.addAll(Arrays.asList(Proxy.class.getMethods())); 054 } 055 056 /** 057 * Creates this adapter. 058 * 059 * @param pojo the pojo to use. 060 */ 061 public AggregationStrategyBeanAdapter(Object pojo) { 062 this(pojo, null); 063 } 064 065 /** 066 * Creates this adapter. 067 * 068 * @param type the class type of the pojo 069 */ 070 public AggregationStrategyBeanAdapter(Class<?> type) { 071 this(type, null); 072 } 073 074 /** 075 * Creates this adapter. 076 * 077 * @param pojo the pojo to use. 078 * @param methodName the name of the method to call 079 */ 080 public AggregationStrategyBeanAdapter(Object pojo, String methodName) { 081 this.pojo = pojo; 082 this.type = pojo.getClass(); 083 this.methodName = methodName; 084 } 085 086 /** 087 * Creates this adapter. 088 * 089 * @param type the class type of the pojo 090 * @param methodName the name of the method to call 091 */ 092 public AggregationStrategyBeanAdapter(Class<?> type, String methodName) { 093 this.type = type; 094 this.pojo = null; 095 this.methodName = methodName; 096 } 097 098 public CamelContext getCamelContext() { 099 return camelContext; 100 } 101 102 public void setCamelContext(CamelContext camelContext) { 103 this.camelContext = camelContext; 104 } 105 106 public String getMethodName() { 107 return methodName; 108 } 109 110 public void setMethodName(String methodName) { 111 this.methodName = methodName; 112 } 113 114 public boolean isAllowNullOldExchange() { 115 return allowNullOldExchange; 116 } 117 118 public void setAllowNullOldExchange(boolean allowNullOldExchange) { 119 this.allowNullOldExchange = allowNullOldExchange; 120 } 121 122 public boolean isAllowNullNewExchange() { 123 return allowNullNewExchange; 124 } 125 126 public void setAllowNullNewExchange(boolean allowNullNewExchange) { 127 this.allowNullNewExchange = allowNullNewExchange; 128 } 129 130 @Override 131 public Exchange aggregate(Exchange oldExchange, Exchange newExchange) { 132 if (!allowNullOldExchange && oldExchange == null) { 133 return newExchange; 134 } 135 if (!allowNullNewExchange && newExchange == null) { 136 return oldExchange; 137 } 138 139 try { 140 Object out = mi.invoke(pojo, oldExchange, newExchange); 141 if (out != null) { 142 if (oldExchange != null) { 143 oldExchange.getIn().setBody(out); 144 } else { 145 newExchange.getIn().setBody(out); 146 } 147 } 148 } catch (Exception e) { 149 if (oldExchange != null) { 150 oldExchange.setException(e); 151 } else { 152 newExchange.setException(e); 153 } 154 } 155 return oldExchange != null ? oldExchange : newExchange; 156 } 157 158 /** 159 * Validates whether the given method is valid. 160 * 161 * @param method the method 162 * @return true if valid, false to skip the method 163 */ 164 protected boolean isValidMethod(Method method) { 165 // must not be in the excluded list 166 for (Method excluded : EXCLUDED_METHODS) { 167 if (method.equals(excluded)) { 168 return false; 169 } 170 } 171 172 // must be a public method 173 if (!Modifier.isPublic(method.getModifiers())) { 174 return false; 175 } 176 177 // return type must not be void and it should not be a bridge method 178 if (method.getReturnType().equals(Void.TYPE) || method.isBridge()) { 179 return false; 180 } 181 182 return true; 183 } 184 185 private static boolean isStaticMethod(Method method) { 186 return Modifier.isStatic(method.getModifiers()); 187 } 188 189 @Override 190 protected void doStart() throws Exception { 191 Method found = null; 192 if (methodName != null) { 193 for (Method method : type.getMethods()) { 194 if (isValidMethod(method) && method.getName().equals(methodName)) { 195 if (found == null) { 196 found = method; 197 } else { 198 throw new IllegalArgumentException("The bean " + type + " has 2 or more methods with the name " + methodName); 199 } 200 } 201 } 202 } else { 203 for (Method method : type.getMethods()) { 204 if (isValidMethod(method)) { 205 if (found == null) { 206 found = method; 207 } else { 208 throw new IllegalArgumentException("The bean " + type + " has 2 or more methods and no explicit method name was configured."); 209 } 210 } 211 } 212 } 213 214 if (found == null) { 215 throw new UnsupportedOperationException("Cannot find a valid method with name: " + methodName + " on bean type: " + type); 216 } 217 218 // if its not a static method then we must have an instance of the pojo 219 if (!isStaticMethod(found) && pojo == null) { 220 pojo = camelContext.getInjector().newInstance(type); 221 } 222 223 // create the method info which has adapted to the pojo 224 AggregationStrategyBeanInfo bi = new AggregationStrategyBeanInfo(type, found); 225 mi = bi.createMethodInfo(); 226 227 // in case the POJO is CamelContextAware 228 if (pojo != null && pojo instanceof CamelContextAware) { 229 ((CamelContextAware) pojo).setCamelContext(getCamelContext()); 230 } 231 232 // in case the pojo is a service 233 ServiceHelper.startService(pojo); 234 } 235 236 @Override 237 protected void doStop() throws Exception { 238 ServiceHelper.stopService(pojo); 239 } 240}