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