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 */
017 package org.apache.camel.model;
018
019 import java.lang.reflect.Method;
020 import java.util.Map;
021 import javax.xml.bind.annotation.XmlAccessType;
022 import javax.xml.bind.annotation.XmlAccessorType;
023 import javax.xml.bind.annotation.XmlAttribute;
024 import javax.xml.bind.annotation.XmlRootElement;
025 import javax.xml.bind.annotation.XmlTransient;
026
027 import org.apache.camel.Processor;
028 import org.apache.camel.RuntimeCamelException;
029 import org.apache.camel.spi.Policy;
030 import org.apache.camel.spi.RouteContext;
031 import org.apache.camel.spi.TransactedPolicy;
032 import org.apache.camel.util.ObjectHelper;
033 import org.apache.commons.logging.Log;
034 import org.apache.commons.logging.LogFactory;
035
036 /**
037 * Represents an XML <transacted/> element
038 *
039 * @version $Revision: 896185 $
040 */
041 @XmlRootElement(name = "transacted")
042 @XmlAccessorType(XmlAccessType.FIELD)
043 public class TransactedDefinition extends OutputDefinition<ProcessorDefinition> {
044
045 // TODO: Align this code with PolicyDefinition
046
047 // JAXB does not support changing the ref attribute from required to optional
048 // if we extend PolicyDefinition so we must make a copy of the class
049 @XmlTransient
050 public static final String PROPAGATION_REQUIRED = "PROPAGATION_REQUIRED";
051
052 private static final transient Log LOG = LogFactory.getLog(TransactedDefinition.class);
053
054 @XmlTransient
055 protected Class<? extends Policy> type = TransactedPolicy.class;
056 @XmlAttribute
057 protected String ref;
058 @XmlTransient
059 private Policy policy;
060
061 public TransactedDefinition() {
062 }
063
064 public TransactedDefinition(Policy policy) {
065 this.policy = policy;
066 }
067
068 @Override
069 public String toString() {
070 return "Transacted[" + description() + "]";
071 }
072
073 @Override
074 public String getShortName() {
075 return "transacted";
076 }
077
078 @Override
079 public String getLabel() {
080 if (ref != null) {
081 return "transacted[ref:" + ref + "]";
082 } else if (policy != null) {
083 return "transacted[" + policy.toString() + "]";
084 } else {
085 return "transacted";
086 }
087 }
088
089 @Override
090 public boolean isAbstract() {
091 return true;
092 }
093
094 public String getRef() {
095 return ref;
096 }
097
098 public void setRef(String ref) {
099 this.ref = ref;
100 }
101
102 /**
103 * Sets a policy type that this definition should scope within.
104 * <p/>
105 * Is used for convention over configuration situations where the policy
106 * should be automatic looked up in the registry and it should be based
107 * on this type. For instance a {@link org.apache.camel.spi.TransactedPolicy}
108 * can be set as type for easy transaction configuration.
109 * <p/>
110 * Will by default scope to the wide {@link Policy}
111 *
112 * @param type the policy type
113 */
114 public void setType(Class<? extends Policy> type) {
115 this.type = type;
116 }
117
118 /**
119 * Sets a reference to use for lookup the policy in the registry.
120 *
121 * @param ref the reference
122 * @return the builder
123 */
124 public TransactedDefinition ref(String ref) {
125 setRef(ref);
126 return this;
127 }
128
129 @Override
130 public Processor createProcessor(RouteContext routeContext) throws Exception {
131 Processor childProcessor = createOutputsProcessor(routeContext);
132
133 Policy policy = resolvePolicy(routeContext);
134 ObjectHelper.notNull(policy, "policy", this);
135 return policy.wrap(routeContext, childProcessor);
136 }
137
138
139 protected String description() {
140 if (policy != null) {
141 return policy.toString();
142 } else {
143 return "ref:" + ref;
144 }
145 }
146
147 protected Policy resolvePolicy(RouteContext routeContext) {
148 if (policy != null) {
149 return policy;
150 }
151 return doResolvePolicy(routeContext, getRef(), type);
152 }
153
154 @SuppressWarnings("unchecked")
155 protected static Policy doResolvePolicy(RouteContext routeContext, String ref, Class<? extends Policy> type) {
156 // explicit ref given so lookup by it
157 if (ObjectHelper.isNotEmpty(ref)) {
158 return routeContext.lookup(ref, Policy.class);
159 }
160
161 // no explicit reference given from user so we can use some convention over configuration here
162
163 // try to lookup by scoped type
164 Policy answer = null;
165 if (type != null) {
166 // try find by type, note that this method is not supported by all registry
167 Map types = routeContext.lookupByType(type);
168 if (types.size() == 1) {
169 // only one policy defined so use it
170 Object found = types.values().iterator().next();
171 if (type.isInstance(found)) {
172 return type.cast(found);
173 }
174 }
175 }
176
177 // for transacted routing try the default REQUIRED name
178 if (type == TransactedPolicy.class) {
179 // still not found try with the default name PROPAGATION_REQUIRED
180 answer = routeContext.lookup(PROPAGATION_REQUIRED, TransactedPolicy.class);
181 }
182
183 // still no policy found then try lookup the platform transaction manager and use it as policy
184 if (answer == null && type == TransactedPolicy.class) {
185 Class tmClazz = routeContext.getCamelContext().getClassResolver().resolveClass("org.springframework.transaction.PlatformTransactionManager");
186 if (tmClazz != null) {
187 // see if we can find the platform transaction manager in the registry
188 Map<String, Object> maps = routeContext.lookupByType(tmClazz);
189 if (maps.size() == 1) {
190 // only one platform manager then use it as default and create a transacted
191 // policy with it and default to required
192
193 // as we do not want dependency on spring jars in the camel-core we use
194 // reflection to lookup classes and create new objects and call methods
195 // as this is only done during route building it does not matter that we
196 // use reflection as performance is no a concern during route building
197 Object transactionManager = maps.values().iterator().next();
198 if (LOG.isDebugEnabled()) {
199 LOG.debug("One instance of PlatformTransactionManager found in registry: " + transactionManager);
200 }
201 Class txClazz = routeContext.getCamelContext().getClassResolver().resolveClass("org.apache.camel.spring.spi.SpringTransactionPolicy");
202 if (txClazz != null) {
203 if (LOG.isDebugEnabled()) {
204 LOG.debug("Creating a new temporary SpringTransactionPolicy using the PlatformTransactionManager: " + transactionManager);
205 }
206 TransactedPolicy txPolicy = ObjectHelper.newInstance(txClazz, TransactedPolicy.class);
207 Method method;
208 try {
209 method = txClazz.getMethod("setTransactionManager", tmClazz);
210 } catch (NoSuchMethodException e) {
211 throw new RuntimeCamelException("Cannot get method setTransactionManager(PlatformTransactionManager) on class: " + txClazz);
212 }
213 ObjectHelper.invokeMethod(method, txPolicy, transactionManager);
214 return txPolicy;
215 } else {
216 LOG.warn("Cannot create a transacted policy as camel-spring.jar is not on the classpath!");
217 }
218 } else {
219 if (LOG.isDebugEnabled()) {
220 if (maps.isEmpty()) {
221 LOG.debug("No PlatformTransactionManager found in registry.");
222 } else {
223 LOG.debug("Found " + maps.size() + " PlatformTransactionManager in registry. "
224 + "Cannot determine which one to use. Please configure a TransactionTemplate on the policy");
225 }
226 }
227 }
228 }
229 }
230
231 return answer;
232 }
233
234 }