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.blueprint.handler;
018
019 import java.lang.reflect.Field;
020 import java.lang.reflect.Method;
021 import java.lang.reflect.Modifier;
022 import java.net.URL;
023 import java.util.Arrays;
024 import java.util.HashSet;
025 import java.util.List;
026 import java.util.Set;
027 import java.util.concurrent.Callable;
028
029 import javax.xml.bind.Binder;
030 import javax.xml.bind.JAXBContext;
031 import javax.xml.bind.JAXBException;
032
033 import org.w3c.dom.Document;
034 import org.w3c.dom.Element;
035 import org.w3c.dom.Node;
036 import org.w3c.dom.NodeList;
037
038 import org.apache.aries.blueprint.BeanProcessor;
039 import org.apache.aries.blueprint.ComponentDefinitionRegistry;
040 import org.apache.aries.blueprint.ComponentDefinitionRegistryProcessor;
041 import org.apache.aries.blueprint.NamespaceHandler;
042 import org.apache.aries.blueprint.ParserContext;
043 import org.apache.aries.blueprint.PassThroughMetadata;
044 import org.apache.aries.blueprint.mutable.MutableBeanMetadata;
045 import org.apache.aries.blueprint.mutable.MutablePassThroughMetadata;
046 import org.apache.aries.blueprint.mutable.MutableRefMetadata;
047 import org.apache.aries.blueprint.mutable.MutableReferenceMetadata;
048 import org.apache.camel.CamelContext;
049 import org.apache.camel.EndpointInject;
050 import org.apache.camel.Produce;
051 import org.apache.camel.PropertyInject;
052 import org.apache.camel.blueprint.BlueprintCamelContext;
053 import org.apache.camel.blueprint.CamelContextFactoryBean;
054 import org.apache.camel.blueprint.CamelRouteContextFactoryBean;
055 import org.apache.camel.builder.xml.Namespaces;
056 import org.apache.camel.core.xml.AbstractCamelContextFactoryBean;
057 import org.apache.camel.core.xml.AbstractCamelFactoryBean;
058 import org.apache.camel.impl.CamelPostProcessorHelper;
059 import org.apache.camel.impl.DefaultCamelContextNameStrategy;
060 import org.apache.camel.model.AggregateDefinition;
061 import org.apache.camel.model.CatchDefinition;
062 import org.apache.camel.model.DataFormatDefinition;
063 import org.apache.camel.model.ExpressionNode;
064 import org.apache.camel.model.ExpressionSubElementDefinition;
065 import org.apache.camel.model.FromDefinition;
066 import org.apache.camel.model.MarshalDefinition;
067 import org.apache.camel.model.OnExceptionDefinition;
068 import org.apache.camel.model.ProcessorDefinition;
069 import org.apache.camel.model.ResequenceDefinition;
070 import org.apache.camel.model.RouteDefinition;
071 import org.apache.camel.model.SendDefinition;
072 import org.apache.camel.model.SortDefinition;
073 import org.apache.camel.model.UnmarshalDefinition;
074 import org.apache.camel.model.WireTapDefinition;
075 import org.apache.camel.model.language.ExpressionDefinition;
076 import org.apache.camel.spi.CamelContextNameStrategy;
077 import org.apache.camel.spi.ComponentResolver;
078 import org.apache.camel.spi.DataFormatResolver;
079 import org.apache.camel.spi.LanguageResolver;
080 import org.apache.camel.spi.NamespaceAware;
081 import org.apache.camel.util.ObjectHelper;
082 import org.apache.camel.util.blueprint.KeyStoreParametersFactoryBean;
083 import org.apache.camel.util.blueprint.SSLContextParametersFactoryBean;
084 import org.apache.camel.util.blueprint.SecureRandomParametersFactoryBean;
085 import org.apache.camel.util.jsse.KeyStoreParameters;
086 import org.apache.camel.util.jsse.SSLContextParameters;
087 import org.apache.camel.util.jsse.SecureRandomParameters;
088
089 import org.osgi.framework.Bundle;
090 import org.osgi.service.blueprint.container.BlueprintContainer;
091 import org.osgi.service.blueprint.container.ComponentDefinitionException;
092 import org.osgi.service.blueprint.reflect.BeanMetadata;
093 import org.osgi.service.blueprint.reflect.ComponentMetadata;
094 import org.osgi.service.blueprint.reflect.Metadata;
095 import org.osgi.service.blueprint.reflect.RefMetadata;
096 import org.slf4j.Logger;
097 import org.slf4j.LoggerFactory;
098
099 import static org.osgi.service.blueprint.reflect.ComponentMetadata.ACTIVATION_LAZY;
100 import static org.osgi.service.blueprint.reflect.ServiceReferenceMetadata.AVAILABILITY_MANDATORY;
101 import static org.osgi.service.blueprint.reflect.ServiceReferenceMetadata.AVAILABILITY_OPTIONAL;
102
103 /**
104 * Camel {@link NamespaceHandler} to parse the Camel related namespaces.
105 */
106 public class CamelNamespaceHandler implements NamespaceHandler {
107
108 public static final String BLUEPRINT_NS = "http://camel.apache.org/schema/blueprint";
109 public static final String SPRING_NS = "http://camel.apache.org/schema/spring";
110
111 private static final String CAMEL_CONTEXT = "camelContext";
112 private static final String ROUTE_CONTEXT = "routeContext";
113 private static final String KEY_STORE_PARAMETERS = "keyStoreParameters";
114 private static final String SECURE_RANDOM_PARAMETERS = "secureRandomParameters";
115 private static final String SSL_CONTEXT_PARAMETERS = "sslContextParameters";
116
117 private static final Logger LOG = LoggerFactory.getLogger(CamelNamespaceHandler.class);
118
119 private JAXBContext jaxbContext;
120
121 public static void renameNamespaceRecursive(Node node, String fromNamespace, String toNamespace) {
122 if (node.getNodeType() == Node.ELEMENT_NODE) {
123 Document doc = node.getOwnerDocument();
124 if (node.getNamespaceURI().equals(fromNamespace)) {
125 doc.renameNode(node, toNamespace, node.getLocalName());
126 }
127 }
128 NodeList list = node.getChildNodes();
129 for (int i = 0; i < list.getLength(); ++i) {
130 renameNamespaceRecursive(list.item(i), fromNamespace, toNamespace);
131 }
132 }
133
134 public URL getSchemaLocation(String namespace) {
135 return getClass().getClassLoader().getResource("camel-blueprint.xsd");
136 }
137
138 @SuppressWarnings({"unchecked", "rawtypes"})
139 public Set<Class> getManagedClasses() {
140 return new HashSet<Class>(Arrays.asList(BlueprintCamelContext.class));
141 }
142
143 public Metadata parse(Element element, ParserContext context) {
144 LOG.trace("Parsing element {}", element);
145
146 try {
147 // as the camel-core model namespace is Spring we need to rename from blueprint to spring
148 renameNamespaceRecursive(element, BLUEPRINT_NS, SPRING_NS);
149
150 if (element.getLocalName().equals(CAMEL_CONTEXT)) {
151 return parseCamelContextNode(element, context);
152 }
153 if (element.getLocalName().equals(ROUTE_CONTEXT)) {
154 return parseRouteContextNode(element, context);
155 }
156 if (element.getLocalName().equals(KEY_STORE_PARAMETERS)) {
157 return parseKeyStoreParametersNode(element, context);
158 }
159 if (element.getLocalName().equals(SECURE_RANDOM_PARAMETERS)) {
160 return parseSecureRandomParametersNode(element, context);
161 }
162 if (element.getLocalName().equals(SSL_CONTEXT_PARAMETERS)) {
163 return parseSSLContextParametersNode(element, context);
164 }
165 } finally {
166 // make sure to rename back so we leave the DOM as-is
167 renameNamespaceRecursive(element, SPRING_NS, BLUEPRINT_NS);
168 }
169
170 return null;
171 }
172
173 private Metadata parseCamelContextNode(Element element, ParserContext context) {
174 LOG.trace("Parsing CamelContext {}", element);
175 // Find the id, generate one if needed
176 String contextId = element.getAttribute("id");
177 boolean implicitId = false;
178
179 // let's avoid folks having to explicitly give an ID to a camel context
180 if (ObjectHelper.isEmpty(contextId)) {
181 // if no explicit id was set then use a default auto generated name
182 CamelContextNameStrategy strategy = new DefaultCamelContextNameStrategy();
183 contextId = strategy.getName();
184 element.setAttributeNS(null, "id", contextId);
185 implicitId = true;
186 }
187
188 // now let's parse the routes with JAXB
189 Binder<Node> binder;
190 try {
191 binder = getJaxbContext().createBinder();
192 } catch (JAXBException e) {
193 throw new ComponentDefinitionException("Failed to create the JAXB binder : " + e, e);
194 }
195 Object value = parseUsingJaxb(element, context, binder);
196 if (!(value instanceof CamelContextFactoryBean)) {
197 throw new ComponentDefinitionException("Expected an instance of " + CamelContextFactoryBean.class);
198 }
199
200 CamelContextFactoryBean ccfb = (CamelContextFactoryBean) value;
201 ccfb.setImplicitId(implicitId);
202
203 // The properties component is always used / created by the CamelContextFactoryBean
204 // so we need to ensure that the resolver is ready to use
205 ComponentMetadata propertiesComponentResolver = getComponentResolverReference(context, "properties");
206
207 MutablePassThroughMetadata factory = context.createMetadata(MutablePassThroughMetadata.class);
208 factory.setId(".camelBlueprint.passThrough." + contextId);
209 factory.setObject(new PassThroughCallable<Object>(value));
210
211 MutableBeanMetadata factory2 = context.createMetadata(MutableBeanMetadata.class);
212 factory2.setId(".camelBlueprint.factory." + contextId);
213 factory2.setFactoryComponent(factory);
214 factory2.setFactoryMethod("call");
215 factory2.setInitMethod("afterPropertiesSet");
216 factory2.setDestroyMethod("destroy");
217 factory2.addProperty("blueprintContainer", createRef(context, "blueprintContainer"));
218 factory2.addProperty("bundleContext", createRef(context, "blueprintBundleContext"));
219 factory2.addDependsOn(propertiesComponentResolver.getId());
220 context.getComponentDefinitionRegistry().registerComponentDefinition(factory2);
221
222 MutableBeanMetadata ctx = context.createMetadata(MutableBeanMetadata.class);
223 ctx.setId(contextId);
224 ctx.setRuntimeClass(BlueprintCamelContext.class);
225 ctx.setFactoryComponent(factory2);
226 ctx.setFactoryMethod("getContext");
227 ctx.setInitMethod("init");
228 ctx.setDestroyMethod("destroy");
229
230 // Register factory beans
231 registerBeans(context, contextId, ccfb.getThreadPools());
232 registerBeans(context, contextId, ccfb.getEndpoints());
233 registerBeans(context, contextId, ccfb.getRedeliveryPolicies());
234 registerBeans(context, contextId, ccfb.getBeans());
235
236 // Register processors
237 MutablePassThroughMetadata beanProcessorFactory = context.createMetadata(MutablePassThroughMetadata.class);
238 beanProcessorFactory.setId(".camelBlueprint.processor.bean.passThrough." + contextId);
239 beanProcessorFactory.setObject(new PassThroughCallable<Object>(new CamelInjector(contextId)));
240
241 MutableBeanMetadata beanProcessor = context.createMetadata(MutableBeanMetadata.class);
242 beanProcessor.setId(".camelBlueprint.processor.bean." + contextId);
243 beanProcessor.setRuntimeClass(CamelInjector.class);
244 beanProcessor.setFactoryComponent(beanProcessorFactory);
245 beanProcessor.setFactoryMethod("call");
246 beanProcessor.setProcessor(true);
247 beanProcessor.addProperty("blueprintContainer", createRef(context, "blueprintContainer"));
248 context.getComponentDefinitionRegistry().registerComponentDefinition(beanProcessor);
249
250 MutablePassThroughMetadata regProcessorFactory = context.createMetadata(MutablePassThroughMetadata.class);
251 regProcessorFactory.setId(".camelBlueprint.processor.registry.passThrough." + contextId);
252 regProcessorFactory.setObject(new PassThroughCallable<Object>(new CamelDependenciesFinder(contextId, context)));
253
254 MutableBeanMetadata regProcessor = context.createMetadata(MutableBeanMetadata.class);
255 regProcessor.setId(".camelBlueprint.processor.registry." + contextId);
256 regProcessor.setRuntimeClass(CamelDependenciesFinder.class);
257 regProcessor.setFactoryComponent(regProcessorFactory);
258 regProcessor.setFactoryMethod("call");
259 regProcessor.setProcessor(true);
260 regProcessor.addDependsOn(".camelBlueprint.processor.bean." + contextId);
261 regProcessor.addProperty("blueprintContainer", createRef(context, "blueprintContainer"));
262 context.getComponentDefinitionRegistry().registerComponentDefinition(regProcessor);
263
264 // lets inject the namespaces into any namespace aware POJOs
265 injectNamespaces(element, binder);
266
267 LOG.trace("Parsing CamelContext done, returning {}", ctx);
268 return ctx;
269 }
270
271 protected void injectNamespaces(Element element, Binder<Node> binder) {
272 NodeList list = element.getChildNodes();
273 Namespaces namespaces = null;
274 int size = list.getLength();
275 for (int i = 0; i < size; i++) {
276 Node child = list.item(i);
277 if (child instanceof Element) {
278 Element childElement = (Element) child;
279 Object object = binder.getJAXBNode(child);
280 if (object instanceof NamespaceAware) {
281 NamespaceAware namespaceAware = (NamespaceAware) object;
282 if (namespaces == null) {
283 namespaces = new Namespaces(element);
284 }
285 namespaces.configure(namespaceAware);
286 }
287 injectNamespaces(childElement, binder);
288 }
289 }
290 }
291
292 private Metadata parseRouteContextNode(Element element, ParserContext context) {
293 LOG.trace("Parsing RouteContext {}", element);
294 // now parse the routes with JAXB
295 Binder<Node> binder;
296 try {
297 binder = getJaxbContext().createBinder();
298 } catch (JAXBException e) {
299 throw new ComponentDefinitionException("Failed to create the JAXB binder : " + e, e);
300 }
301 Object value = parseUsingJaxb(element, context, binder);
302 if (!(value instanceof CamelRouteContextFactoryBean)) {
303 throw new ComponentDefinitionException("Expected an instance of " + CamelRouteContextFactoryBean.class);
304 }
305
306 CamelRouteContextFactoryBean rcfb = (CamelRouteContextFactoryBean) value;
307 String id = rcfb.getId();
308
309 MutablePassThroughMetadata factory = context.createMetadata(MutablePassThroughMetadata.class);
310 factory.setId(".camelBlueprint.passThrough." + id);
311 factory.setObject(new PassThroughCallable<Object>(rcfb));
312
313 MutableBeanMetadata factory2 = context.createMetadata(MutableBeanMetadata.class);
314 factory2.setId(".camelBlueprint.factory." + id);
315 factory2.setFactoryComponent(factory);
316 factory2.setFactoryMethod("call");
317
318 MutableBeanMetadata ctx = context.createMetadata(MutableBeanMetadata.class);
319 ctx.setId(id);
320 ctx.setRuntimeClass(List.class);
321 ctx.setFactoryComponent(factory2);
322 ctx.setFactoryMethod("getRoutes");
323 // must be lazy as we want CamelContext to be activated first
324 ctx.setActivation(ACTIVATION_LAZY);
325
326 // lets inject the namespaces into any namespace aware POJOs
327 injectNamespaces(element, binder);
328
329 LOG.trace("Parsing RouteContext done, returning {}", element, ctx);
330 return ctx;
331 }
332
333 private Metadata parseKeyStoreParametersNode(Element element, ParserContext context) {
334 LOG.trace("Parsing KeyStoreParameters {}", element);
335 // now parse the key store parameters with JAXB
336 Binder<Node> binder;
337 try {
338 binder = getJaxbContext().createBinder();
339 } catch (JAXBException e) {
340 throw new ComponentDefinitionException("Failed to create the JAXB binder : " + e, e);
341 }
342 Object value = parseUsingJaxb(element, context, binder);
343 if (!(value instanceof KeyStoreParametersFactoryBean)) {
344 throw new ComponentDefinitionException("Expected an instance of " + KeyStoreParametersFactoryBean.class);
345 }
346
347 KeyStoreParametersFactoryBean kspfb = (KeyStoreParametersFactoryBean) value;
348 String id = kspfb.getId();
349
350 MutablePassThroughMetadata factory = context.createMetadata(MutablePassThroughMetadata.class);
351 factory.setId(".camelBlueprint.passThrough." + id);
352 factory.setObject(new PassThroughCallable<Object>(kspfb));
353
354 MutableBeanMetadata factory2 = context.createMetadata(MutableBeanMetadata.class);
355 factory2.setId(".camelBlueprint.factory." + id);
356 factory2.setFactoryComponent(factory);
357 factory2.setFactoryMethod("call");
358 factory2.setInitMethod("afterPropertiesSet");
359 factory2.setDestroyMethod("destroy");
360 factory2.addProperty("blueprintContainer", createRef(context, "blueprintContainer"));
361
362 MutableBeanMetadata ctx = context.createMetadata(MutableBeanMetadata.class);
363 ctx.setId(id);
364 ctx.setRuntimeClass(KeyStoreParameters.class);
365 ctx.setFactoryComponent(factory2);
366 ctx.setFactoryMethod("getObject");
367 // must be lazy as we want CamelContext to be activated first
368 ctx.setActivation(ACTIVATION_LAZY);
369
370 LOG.trace("Parsing KeyStoreParameters done, returning {}", ctx);
371 return ctx;
372 }
373
374 private Metadata parseSecureRandomParametersNode(Element element, ParserContext context) {
375 LOG.trace("Parsing SecureRandomParameters {}", element);
376 // now parse the key store parameters with JAXB
377 Binder<Node> binder;
378 try {
379 binder = getJaxbContext().createBinder();
380 } catch (JAXBException e) {
381 throw new ComponentDefinitionException("Failed to create the JAXB binder : " + e, e);
382 }
383 Object value = parseUsingJaxb(element, context, binder);
384 if (!(value instanceof SecureRandomParametersFactoryBean)) {
385 throw new ComponentDefinitionException("Expected an instance of " + SecureRandomParametersFactoryBean.class);
386 }
387
388 SecureRandomParametersFactoryBean srfb = (SecureRandomParametersFactoryBean) value;
389 String id = srfb.getId();
390
391 MutablePassThroughMetadata factory = context.createMetadata(MutablePassThroughMetadata.class);
392 factory.setId(".camelBlueprint.passThrough." + id);
393 factory.setObject(new PassThroughCallable<Object>(srfb));
394
395 MutableBeanMetadata factory2 = context.createMetadata(MutableBeanMetadata.class);
396 factory2.setId(".camelBlueprint.factory." + id);
397 factory2.setFactoryComponent(factory);
398 factory2.setFactoryMethod("call");
399 factory2.setInitMethod("afterPropertiesSet");
400 factory2.setDestroyMethod("destroy");
401 factory2.addProperty("blueprintContainer", createRef(context, "blueprintContainer"));
402
403 MutableBeanMetadata ctx = context.createMetadata(MutableBeanMetadata.class);
404 ctx.setId(id);
405 ctx.setRuntimeClass(SecureRandomParameters.class);
406 ctx.setFactoryComponent(factory2);
407 ctx.setFactoryMethod("getObject");
408 // must be lazy as we want CamelContext to be activated first
409 ctx.setActivation(ACTIVATION_LAZY);
410
411 LOG.trace("Parsing SecureRandomParameters done, returning {}", ctx);
412 return ctx;
413 }
414
415 private Metadata parseSSLContextParametersNode(Element element, ParserContext context) {
416 LOG.trace("Parsing SSLContextParameters {}", element);
417 // now parse the key store parameters with JAXB
418 Binder<Node> binder;
419 try {
420 binder = getJaxbContext().createBinder();
421 } catch (JAXBException e) {
422 throw new ComponentDefinitionException("Failed to create the JAXB binder : " + e, e);
423 }
424 Object value = parseUsingJaxb(element, context, binder);
425 if (!(value instanceof SSLContextParametersFactoryBean)) {
426 throw new ComponentDefinitionException("Expected an instance of " + SSLContextParametersFactoryBean.class);
427 }
428
429 SSLContextParametersFactoryBean scpfb = (SSLContextParametersFactoryBean) value;
430 String id = scpfb.getId();
431
432 MutablePassThroughMetadata factory = context.createMetadata(MutablePassThroughMetadata.class);
433 factory.setId(".camelBlueprint.passThrough." + id);
434 factory.setObject(new PassThroughCallable<Object>(scpfb));
435
436 MutableBeanMetadata factory2 = context.createMetadata(MutableBeanMetadata.class);
437 factory2.setId(".camelBlueprint.factory." + id);
438 factory2.setFactoryComponent(factory);
439 factory2.setFactoryMethod("call");
440 factory2.setInitMethod("afterPropertiesSet");
441 factory2.setDestroyMethod("destroy");
442 factory2.addProperty("blueprintContainer", createRef(context, "blueprintContainer"));
443
444 MutableBeanMetadata ctx = context.createMetadata(MutableBeanMetadata.class);
445 ctx.setId(id);
446 ctx.setRuntimeClass(SSLContextParameters.class);
447 ctx.setFactoryComponent(factory2);
448 ctx.setFactoryMethod("getObject");
449 // must be lazy as we want CamelContext to be activated first
450 ctx.setActivation(ACTIVATION_LAZY);
451
452 LOG.trace("Parsing SSLContextParameters done, returning {}", ctx);
453 return ctx;
454 }
455
456 private void registerBeans(ParserContext context, String contextId, List<?> beans) {
457 if (beans != null) {
458 for (Object bean : beans) {
459 if (bean instanceof AbstractCamelFactoryBean) {
460 registerBean(context, contextId, (AbstractCamelFactoryBean<?>) bean);
461 }
462 }
463 }
464 }
465
466 protected void registerBean(ParserContext context, String contextId, AbstractCamelFactoryBean<?> fact) {
467 String id = fact.getId();
468
469 fact.setCamelContextId(contextId);
470
471 MutablePassThroughMetadata eff = context.createMetadata(MutablePassThroughMetadata.class);
472 eff.setId(".camelBlueprint.bean.passthrough." + id);
473 eff.setObject(new PassThroughCallable<Object>(fact));
474
475 MutableBeanMetadata ef = context.createMetadata(MutableBeanMetadata.class);
476 ef.setId(".camelBlueprint.bean.factory." + id);
477 ef.setFactoryComponent(eff);
478 ef.setFactoryMethod("call");
479 ef.addProperty("blueprintContainer", createRef(context, "blueprintContainer"));
480 ef.setInitMethod("afterPropertiesSet");
481 ef.setDestroyMethod("destroy");
482
483 MutableBeanMetadata e = context.createMetadata(MutableBeanMetadata.class);
484 e.setId(id);
485 e.setRuntimeClass(fact.getObjectType());
486 e.setFactoryComponent(ef);
487 e.setFactoryMethod("getObject");
488 e.addDependsOn(".camelBlueprint.processor.bean." + contextId);
489
490 context.getComponentDefinitionRegistry().registerComponentDefinition(e);
491 }
492
493 protected BlueprintContainer getBlueprintContainer(ParserContext context) {
494 PassThroughMetadata ptm = (PassThroughMetadata) context.getComponentDefinitionRegistry().getComponentDefinition("blueprintContainer");
495 return (BlueprintContainer) ptm.getObject();
496 }
497
498 public ComponentMetadata decorate(Node node, ComponentMetadata component, ParserContext context) {
499 return null;
500 }
501
502 protected Object parseUsingJaxb(Element element, ParserContext parserContext, Binder<Node> binder) {
503 try {
504 return binder.unmarshal(element);
505 } catch (JAXBException e) {
506 throw new ComponentDefinitionException("Failed to parse JAXB element: " + e, e);
507 }
508 }
509
510 public JAXBContext getJaxbContext() throws JAXBException {
511 if (jaxbContext == null) {
512 jaxbContext = createJaxbContext();
513 }
514 return jaxbContext;
515 }
516
517 protected JAXBContext createJaxbContext() throws JAXBException {
518 StringBuilder packages = new StringBuilder();
519 for (Class<?> cl : getJaxbPackages()) {
520 if (packages.length() > 0) {
521 packages.append(":");
522 }
523 packages.append(cl.getName().substring(0, cl.getName().lastIndexOf('.')));
524 }
525 return JAXBContext.newInstance(packages.toString(), getClass().getClassLoader());
526 }
527
528 protected Set<Class<?>> getJaxbPackages() {
529 Set<Class<?>> classes = new HashSet<Class<?>>();
530 classes.add(CamelContextFactoryBean.class);
531 classes.add(AbstractCamelContextFactoryBean.class);
532 classes.add(org.apache.camel.ExchangePattern.class);
533 classes.add(org.apache.camel.model.RouteDefinition.class);
534 classes.add(org.apache.camel.model.config.StreamResequencerConfig.class);
535 classes.add(org.apache.camel.model.dataformat.DataFormatsDefinition.class);
536 classes.add(org.apache.camel.model.language.ExpressionDefinition.class);
537 classes.add(org.apache.camel.model.loadbalancer.RoundRobinLoadBalancerDefinition.class);
538 classes.add(SSLContextParametersFactoryBean.class);
539 return classes;
540 }
541
542 private RefMetadata createRef(ParserContext context, String value) {
543 MutableRefMetadata r = context.createMetadata(MutableRefMetadata.class);
544 r.setComponentId(value);
545 return r;
546 }
547
548 private static ComponentMetadata getDataformatResolverReference(ParserContext context, String dataformat) {
549 ComponentDefinitionRegistry componentDefinitionRegistry = context.getComponentDefinitionRegistry();
550 ComponentMetadata cm = componentDefinitionRegistry.getComponentDefinition(".camelBlueprint.dataformatResolver." + dataformat);
551 if (cm == null) {
552 MutableReferenceMetadata svc = context.createMetadata(MutableReferenceMetadata.class);
553 svc.setId(".camelBlueprint.dataformatResolver." + dataformat);
554 svc.setFilter("(dataformat=" + dataformat + ")");
555 svc.setAvailability(componentDefinitionRegistry.containsComponentDefinition(dataformat) ? AVAILABILITY_OPTIONAL : AVAILABILITY_MANDATORY);
556 try {
557 // Try to set the runtime interface (only with aries blueprint > 0.1
558 svc.getClass().getMethod("setRuntimeInterface", Class.class).invoke(svc, DataFormatResolver.class);
559 } catch (Throwable t) {
560 // Check if the bundle can see the class
561 try {
562 PassThroughMetadata ptm = (PassThroughMetadata) componentDefinitionRegistry.getComponentDefinition("blueprintBundle");
563 Bundle b = (Bundle) ptm.getObject();
564 if (b.loadClass(DataFormatResolver.class.getName()) != DataFormatResolver.class) {
565 throw new UnsupportedOperationException();
566 }
567 svc.setInterface(DataFormatResolver.class.getName());
568 } catch (Throwable t2) {
569 throw new UnsupportedOperationException();
570 }
571 }
572 componentDefinitionRegistry.registerComponentDefinition(svc);
573 cm = svc;
574 }
575 return cm;
576 }
577
578 private static ComponentMetadata getLanguageResolverReference(ParserContext context, String language) {
579 ComponentDefinitionRegistry componentDefinitionRegistry = context.getComponentDefinitionRegistry();
580 ComponentMetadata cm = componentDefinitionRegistry.getComponentDefinition(".camelBlueprint.languageResolver." + language);
581 if (cm == null) {
582 MutableReferenceMetadata svc = context.createMetadata(MutableReferenceMetadata.class);
583 svc.setId(".camelBlueprint.languageResolver." + language);
584 svc.setFilter("(language=" + language + ")");
585 svc.setAvailability(componentDefinitionRegistry.containsComponentDefinition(language) ? AVAILABILITY_OPTIONAL : AVAILABILITY_MANDATORY);
586 try {
587 // Try to set the runtime interface (only with aries blueprint > 0.1
588 svc.getClass().getMethod("setRuntimeInterface", Class.class).invoke(svc, LanguageResolver.class);
589 } catch (Throwable t) {
590 // Check if the bundle can see the class
591 try {
592 PassThroughMetadata ptm = (PassThroughMetadata) componentDefinitionRegistry.getComponentDefinition("blueprintBundle");
593 Bundle b = (Bundle) ptm.getObject();
594 if (b.loadClass(LanguageResolver.class.getName()) != LanguageResolver.class) {
595 throw new UnsupportedOperationException();
596 }
597 svc.setInterface(LanguageResolver.class.getName());
598 } catch (Throwable t2) {
599 throw new UnsupportedOperationException();
600 }
601 }
602 componentDefinitionRegistry.registerComponentDefinition(svc);
603 cm = svc;
604 }
605 return cm;
606 }
607
608 private static ComponentMetadata getComponentResolverReference(ParserContext context, String component) {
609 ComponentDefinitionRegistry componentDefinitionRegistry = context.getComponentDefinitionRegistry();
610 ComponentMetadata cm = componentDefinitionRegistry.getComponentDefinition(".camelBlueprint.componentResolver." + component);
611 if (cm == null) {
612 MutableReferenceMetadata svc = context.createMetadata(MutableReferenceMetadata.class);
613 svc.setId(".camelBlueprint.componentResolver." + component);
614 svc.setFilter("(component=" + component + ")");
615 svc.setAvailability(componentDefinitionRegistry.containsComponentDefinition(component) ? AVAILABILITY_OPTIONAL : AVAILABILITY_MANDATORY);
616 try {
617 // Try to set the runtime interface (only with aries blueprint > 0.1
618 svc.getClass().getMethod("setRuntimeInterface", Class.class).invoke(svc, ComponentResolver.class);
619 } catch (Throwable t) {
620 // Check if the bundle can see the class
621 try {
622 PassThroughMetadata ptm = (PassThroughMetadata) componentDefinitionRegistry.getComponentDefinition("blueprintBundle");
623 Bundle b = (Bundle) ptm.getObject();
624 if (b.loadClass(ComponentResolver.class.getName()) != ComponentResolver.class) {
625 throw new UnsupportedOperationException();
626 }
627 svc.setInterface(ComponentResolver.class.getName());
628 } catch (Throwable t2) {
629 throw new UnsupportedOperationException();
630 }
631 }
632 componentDefinitionRegistry.registerComponentDefinition(svc);
633 cm = svc;
634 }
635 return cm;
636 }
637
638 public static class PassThroughCallable<T> implements Callable<T> {
639
640 private T value;
641
642 public PassThroughCallable(T value) {
643 this.value = value;
644 }
645
646 public T call() throws Exception {
647 return value;
648 }
649 }
650
651 public static class CamelInjector extends CamelPostProcessorHelper implements BeanProcessor {
652
653 private final String camelContextName;
654 private BlueprintContainer blueprintContainer;
655
656 public CamelInjector(String camelContextName) {
657 this.camelContextName = camelContextName;
658 }
659
660 public void setBlueprintContainer(BlueprintContainer blueprintContainer) {
661 this.blueprintContainer = blueprintContainer;
662 }
663
664 @Override
665 public CamelContext getCamelContext() {
666 if (blueprintContainer != null) {
667 CamelContext answer = (CamelContext) blueprintContainer.getComponentInstance(camelContextName);
668 return answer;
669 }
670 return null;
671 }
672
673 public Object beforeInit(Object bean, String beanName, BeanCreator beanCreator, BeanMetadata beanMetadata) {
674 LOG.trace("Before init of bean: {} -> {}", beanName, bean);
675 // prefer to inject later in afterInit
676 return bean;
677 }
678
679 /**
680 * A strategy method to allow implementations to perform some custom JBI
681 * based injection of the POJO
682 *
683 * @param bean the bean to be injected
684 */
685 protected void injectFields(final Object bean, final String beanName) {
686 Class<?> clazz = bean.getClass();
687 do {
688 Field[] fields = clazz.getDeclaredFields();
689 for (Field field : fields) {
690 PropertyInject propertyInject = field.getAnnotation(PropertyInject.class);
691 if (propertyInject != null && matchContext(propertyInject.context())) {
692 injectFieldProperty(field, propertyInject.value(), propertyInject.defaultValue(), bean, beanName);
693 }
694
695 EndpointInject endpointInject = field.getAnnotation(EndpointInject.class);
696 if (endpointInject != null && matchContext(endpointInject.context())) {
697 injectField(field, endpointInject.uri(), endpointInject.ref(), endpointInject.property(), bean, beanName);
698 }
699
700 Produce produce = field.getAnnotation(Produce.class);
701 if (produce != null && matchContext(produce.context())) {
702 injectField(field, produce.uri(), produce.ref(), produce.property(), bean, beanName);
703 }
704 }
705 clazz = clazz.getSuperclass();
706 } while (clazz != null && clazz != Object.class);
707 }
708
709 protected void injectField(Field field, String endpointUri, String endpointRef, String endpointProperty, Object bean, String beanName) {
710 setField(field, bean, getInjectionValue(field.getType(), endpointUri, endpointRef, endpointProperty, field.getName(), bean, beanName));
711 }
712
713 protected void injectFieldProperty(Field field, String propertyName, String propertyDefaultValue, Object bean, String beanName) {
714 setField(field, bean, getInjectionPropertyValue(field.getType(), propertyName, propertyDefaultValue, field.getName(), bean, beanName));
715 }
716
717 protected static void setField(Field field, Object instance, Object value) {
718 try {
719 boolean oldAccessible = field.isAccessible();
720 boolean shouldSetAccessible = !Modifier.isPublic(field.getModifiers()) && !oldAccessible;
721 if (shouldSetAccessible) {
722 field.setAccessible(true);
723 }
724 field.set(instance, value);
725 if (shouldSetAccessible) {
726 field.setAccessible(oldAccessible);
727 }
728 } catch (IllegalArgumentException ex) {
729 throw new UnsupportedOperationException("Cannot inject value of class: " + value.getClass() + " into: " + field);
730 } catch (IllegalAccessException ex) {
731 throw new IllegalStateException("Could not access method: " + ex.getMessage());
732 }
733 }
734
735 protected void injectMethods(final Object bean, final String beanName) {
736 Class<?> clazz = bean.getClass();
737 do {
738 Method[] methods = clazz.getDeclaredMethods();
739 for (Method method : methods) {
740 setterInjection(method, bean, beanName);
741 consumerInjection(method, bean, beanName);
742 }
743 clazz = clazz.getSuperclass();
744 } while (clazz != null && clazz != Object.class);
745 }
746
747 protected void setterInjection(Method method, Object bean, String beanName) {
748 PropertyInject propertyInject = method.getAnnotation(PropertyInject.class);
749 if (propertyInject != null && matchContext(propertyInject.context())) {
750 setterPropertyInjection(method, propertyInject.value(), propertyInject.defaultValue(), bean, beanName);
751 }
752
753 EndpointInject endpointInject = method.getAnnotation(EndpointInject.class);
754 if (endpointInject != null && matchContext(endpointInject.context())) {
755 setterInjection(method, bean, beanName, endpointInject.uri(), endpointInject.ref(), endpointInject.property());
756 }
757
758 Produce produce = method.getAnnotation(Produce.class);
759 if (produce != null && matchContext(produce.context())) {
760 setterInjection(method, bean, beanName, produce.uri(), produce.ref(), produce.property());
761 }
762 }
763
764 protected void setterPropertyInjection(Method method, String propertyValue, String propertyDefaultValue, Object bean, String beanName) {
765 Class<?>[] parameterTypes = method.getParameterTypes();
766 if (parameterTypes != null) {
767 if (parameterTypes.length != 1) {
768 LOG.warn("Ignoring badly annotated method for injection due to incorrect number of parameters: " + method);
769 } else {
770 String propertyName = ObjectHelper.getPropertyName(method);
771 Object value = getInjectionPropertyValue(parameterTypes[0], propertyValue, propertyDefaultValue, propertyName, bean, beanName);
772 ObjectHelper.invokeMethod(method, bean, value);
773 }
774 }
775 }
776
777 protected void setterInjection(Method method, Object bean, String beanName, String endpointUri, String endpointRef, String endpointProperty) {
778 Class<?>[] parameterTypes = method.getParameterTypes();
779 if (parameterTypes != null) {
780 if (parameterTypes.length != 1) {
781 LOG.warn("Ignoring badly annotated method for injection due to incorrect number of parameters: " + method);
782 } else {
783 String propertyName = ObjectHelper.getPropertyName(method);
784 Object value = getInjectionValue(parameterTypes[0], endpointUri, endpointRef, endpointProperty, propertyName, bean, beanName);
785 ObjectHelper.invokeMethod(method, bean, value);
786 }
787 }
788 }
789
790 public Object afterInit(Object bean, String beanName, BeanCreator beanCreator, BeanMetadata beanMetadata) {
791 LOG.trace("After init of bean: {} -> {}", beanName, bean);
792 // we cannot inject CamelContextAware beans as the CamelContext may not be ready
793 injectFields(bean, beanName);
794 injectMethods(bean, beanName);
795 return bean;
796 }
797
798 public void beforeDestroy(Object bean, String beanName) {
799 }
800
801 public void afterDestroy(Object bean, String beanName) {
802 }
803
804 @Override
805 protected boolean isSingleton(Object bean, String beanName) {
806 ComponentMetadata meta = blueprintContainer.getComponentMetadata(beanName);
807 if (meta != null && meta instanceof BeanMetadata) {
808 String scope = ((BeanMetadata) meta).getScope();
809 if (scope != null) {
810 return BeanMetadata.SCOPE_SINGLETON.equals(scope);
811 }
812 }
813 // fallback to super, which will assume singleton
814 // for beans not implementing Camel's IsSingleton interface
815 return super.isSingleton(bean, beanName);
816 }
817 }
818
819 public static class CamelDependenciesFinder implements ComponentDefinitionRegistryProcessor {
820
821 private final String camelContextName;
822 private final ParserContext context;
823 private BlueprintContainer blueprintContainer;
824
825 public CamelDependenciesFinder(String camelContextName, ParserContext context) {
826 this.camelContextName = camelContextName;
827 this.context = context;
828 }
829
830 public void setBlueprintContainer(BlueprintContainer blueprintContainer) {
831 this.blueprintContainer = blueprintContainer;
832 }
833
834 @SuppressWarnings("deprecation")
835 public void process(ComponentDefinitionRegistry componentDefinitionRegistry) {
836 CamelContextFactoryBean ccfb = (CamelContextFactoryBean) blueprintContainer.getComponentInstance(".camelBlueprint.factory." + camelContextName);
837 CamelContext camelContext = ccfb.getContext();
838
839 Set<String> components = new HashSet<String>();
840 Set<String> languages = new HashSet<String>();
841 Set<String> dataformats = new HashSet<String>();
842 for (RouteDefinition rd : camelContext.getRouteDefinitions()) {
843 findInputComponents(rd.getInputs(), components, languages, dataformats);
844 findOutputComponents(rd.getOutputs(), components, languages, dataformats);
845 }
846 // We can only add service references to resolvers, but we can't make the factory depends on those
847 // because the factory has already been instantiated
848 try {
849 for (String component : components) {
850 getComponentResolverReference(context, component);
851 }
852 for (String language : languages) {
853 getLanguageResolverReference(context, language);
854 }
855 for (String dataformat : dataformats) {
856 getDataformatResolverReference(context, dataformat);
857 }
858 } catch (UnsupportedOperationException e) {
859 LOG.warn("Unable to add dependencies to Camel components OSGi services. "
860 + "The Apache Aries blueprint implementation used is too old and the blueprint bundle can not see the org.apache.camel.spi package.");
861 components.clear();
862 languages.clear();
863 dataformats.clear();
864 }
865
866 }
867
868 private void findInputComponents(List<FromDefinition> defs, Set<String> components, Set<String> languages, Set<String> dataformats) {
869 if (defs != null) {
870 for (FromDefinition def : defs) {
871 findUriComponent(def.getUri(), components);
872 }
873 }
874 }
875
876 @SuppressWarnings({"rawtypes"})
877 private void findOutputComponents(List<ProcessorDefinition<?>> defs, Set<String> components, Set<String> languages, Set<String> dataformats) {
878 if (defs != null) {
879 for (ProcessorDefinition<?> def : defs) {
880 if (def instanceof SendDefinition) {
881 findUriComponent(((SendDefinition) def).getUri(), components);
882 }
883 if (def instanceof MarshalDefinition) {
884 findDataFormat(((MarshalDefinition) def).getDataFormatType(), dataformats);
885 }
886 if (def instanceof UnmarshalDefinition) {
887 findDataFormat(((UnmarshalDefinition) def).getDataFormatType(), dataformats);
888 }
889 if (def instanceof ExpressionNode) {
890 findLanguage(((ExpressionNode) def).getExpression(), languages);
891 }
892 if (def instanceof ResequenceDefinition) {
893 findLanguage(((ResequenceDefinition) def).getExpression(), languages);
894 }
895 if (def instanceof AggregateDefinition) {
896 findLanguage(((AggregateDefinition) def).getExpression(), languages);
897 findLanguage(((AggregateDefinition) def).getCorrelationExpression(), languages);
898 findLanguage(((AggregateDefinition) def).getCompletionPredicate(), languages);
899 findLanguage(((AggregateDefinition) def).getCompletionTimeoutExpression(), languages);
900 findLanguage(((AggregateDefinition) def).getCompletionSizeExpression(), languages);
901 }
902 if (def instanceof CatchDefinition) {
903 findLanguage(((CatchDefinition) def).getHandled(), languages);
904 }
905 if (def instanceof OnExceptionDefinition) {
906 findLanguage(((OnExceptionDefinition) def).getRetryWhile(), languages);
907 findLanguage(((OnExceptionDefinition) def).getHandled(), languages);
908 findLanguage(((OnExceptionDefinition) def).getContinued(), languages);
909 }
910 if (def instanceof SortDefinition) {
911 findLanguage(((SortDefinition) def).getExpression(), languages);
912 }
913 if (def instanceof WireTapDefinition) {
914 findLanguage(((WireTapDefinition<?>) def).getNewExchangeExpression(), languages);
915 }
916 findOutputComponents(def.getOutputs(), components, languages, dataformats);
917 }
918 }
919 }
920
921 private void findLanguage(ExpressionDefinition expression, Set<String> languages) {
922 if (expression != null) {
923 String lang = expression.getLanguage();
924 if (lang != null && lang.length() > 0) {
925 languages.add(lang);
926 }
927 }
928 }
929
930 private void findLanguage(ExpressionSubElementDefinition expression, Set<String> languages) {
931 if (expression != null) {
932 findLanguage(expression.getExpressionType(), languages);
933 }
934 }
935
936 private void findDataFormat(DataFormatDefinition dfd, Set<String> dataformats) {
937 if (dfd != null && dfd.getDataFormatName() != null) {
938 dataformats.add(dfd.getDataFormatName());
939 }
940 }
941
942 private void findUriComponent(String uri, Set<String> components) {
943 if (uri != null) {
944 String splitURI[] = ObjectHelper.splitOnCharacter(uri, ":", 2);
945 if (splitURI[1] != null) {
946 String scheme = splitURI[0];
947 components.add(scheme);
948 }
949 }
950 }
951
952 }
953
954 }