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.net.URL;
020 import java.util.ArrayList;
021 import java.util.Arrays;
022 import java.util.HashSet;
023 import java.util.List;
024 import java.util.Set;
025 import java.util.concurrent.Callable;
026
027 import javax.xml.bind.Binder;
028 import javax.xml.bind.JAXBContext;
029 import javax.xml.bind.JAXBException;
030
031 import org.w3c.dom.Document;
032 import org.w3c.dom.Element;
033 import org.w3c.dom.Node;
034 import org.w3c.dom.NodeList;
035
036 import org.apache.aries.blueprint.NamespaceHandler;
037 import org.apache.aries.blueprint.ParserContext;
038 import org.apache.aries.blueprint.PassThroughMetadata;
039 import org.apache.aries.blueprint.mutable.MutableBeanMetadata;
040 import org.apache.aries.blueprint.mutable.MutablePassThroughMetadata;
041 import org.apache.aries.blueprint.mutable.MutableReferenceMetadata;
042 import org.apache.camel.blueprint.BlueprintCamelContext;
043 import org.apache.camel.blueprint.CamelContextFactoryBean;
044 import org.apache.camel.core.xml.AbstractCamelContextFactoryBean;
045 import org.apache.camel.impl.DefaultCamelContextNameStrategy;
046 import org.apache.camel.model.AggregateDefinition;
047 import org.apache.camel.model.CatchDefinition;
048 import org.apache.camel.model.DataFormatDefinition;
049 import org.apache.camel.model.ExpressionNode;
050 import org.apache.camel.model.ExpressionSubElementDefinition;
051 import org.apache.camel.model.FromDefinition;
052 import org.apache.camel.model.MarshalDefinition;
053 import org.apache.camel.model.OnExceptionDefinition;
054 import org.apache.camel.model.ProcessorDefinition;
055 import org.apache.camel.model.ResequenceDefinition;
056 import org.apache.camel.model.RouteDefinition;
057 import org.apache.camel.model.SendDefinition;
058 import org.apache.camel.model.SortDefinition;
059 import org.apache.camel.model.UnmarshalDefinition;
060 import org.apache.camel.model.WireTapDefinition;
061 import org.apache.camel.model.language.ExpressionDefinition;
062 import org.apache.camel.spi.ComponentResolver;
063 import org.apache.camel.spi.DataFormatResolver;
064 import org.apache.camel.spi.LanguageResolver;
065 import org.apache.camel.util.ObjectHelper;
066 import org.apache.commons.logging.Log;
067 import org.apache.commons.logging.LogFactory;
068 import org.osgi.framework.Bundle;
069 import org.osgi.framework.BundleContext;
070 import org.osgi.service.blueprint.container.BlueprintContainer;
071 import org.osgi.service.blueprint.container.ComponentDefinitionException;
072 import org.osgi.service.blueprint.reflect.ComponentMetadata;
073 import org.osgi.service.blueprint.reflect.Metadata;
074
075 public class CamelNamespaceHandler implements NamespaceHandler {
076
077 private static final String CAMEL_CONTEXT = "camelContext";
078
079 private static final String SPRING_NS = "http://camel.apache.org/schema/spring";
080 private static final String BLUEPRINT_NS = "http://camel.apache.org/schema/blueprint";
081
082 private static final transient Log LOG = LogFactory.getLog(CamelNamespaceHandler.class);
083
084 private JAXBContext jaxbContext;
085
086 public static void renameNamespaceRecursive(Node node) {
087 if (node.getNodeType() == Node.ELEMENT_NODE) {
088 Document doc = node.getOwnerDocument();
089 if (((Element) node).getNamespaceURI().equals(BLUEPRINT_NS)) {
090 doc.renameNode(node, SPRING_NS, node.getNodeName());
091 }
092 }
093 NodeList list = node.getChildNodes();
094 for (int i = 0; i < list.getLength(); ++i) {
095 renameNamespaceRecursive(list.item(i));
096 }
097 }
098
099 public URL getSchemaLocation(String namespace) {
100 return getClass().getClassLoader().getResource("camel-blueprint.xsd");
101 }
102
103 @SuppressWarnings("unchecked")
104 public Set<Class> getManagedClasses() {
105 return new HashSet<Class>(Arrays.asList(
106 BlueprintCamelContext.class
107 ));
108 }
109
110 public Metadata parse(Element element, ParserContext context) {
111 renameNamespaceRecursive(element);
112 if (element.getNodeName().equals(CAMEL_CONTEXT)) {
113 // Find the id, generate one if needed
114 String contextId = element.getAttribute("id");
115 boolean implicitId = false;
116
117 // lets avoid folks having to explicitly give an ID to a camel context
118 if (ObjectHelper.isEmpty(contextId)) {
119 // if no explicit id was set then use a default auto generated name
120 contextId = DefaultCamelContextNameStrategy.getNextName();
121 element.setAttribute("id", contextId);
122 implicitId = true;
123 }
124
125 // now lets parse the routes with JAXB
126 Binder<Node> binder;
127 try {
128 binder = getJaxbContext().createBinder();
129 } catch (JAXBException e) {
130 throw new ComponentDefinitionException("Failed to create the JAXB binder : " + e, e);
131 }
132 Object value = parseUsingJaxb(element, context, binder);
133 if (!(value instanceof CamelContextFactoryBean)) {
134 throw new ComponentDefinitionException("Expected an instance of " + CamelContextFactoryBean.class);
135 }
136
137 CamelContextFactoryBean ccfb = (CamelContextFactoryBean) value;
138 try {
139 PassThroughMetadata ptm = (PassThroughMetadata) context.getComponentDefinitionRegistry().getComponentDefinition("blueprintContainer");
140 ccfb.setBlueprintContainer((BlueprintContainer) ptm.getObject());
141 ptm = (PassThroughMetadata) context.getComponentDefinitionRegistry().getComponentDefinition("blueprintBundleContext");
142 ccfb.setBundleContext((BundleContext) ptm.getObject());
143 ccfb.setImplicitId(implicitId);
144 ccfb.afterPropertiesSet();
145 } catch (Exception e) {
146 throw new ComponentDefinitionException("Unable to initialize camel context factory", e);
147 }
148
149 Set<String> components = new HashSet<String>();
150 Set<String> languages = new HashSet<String>();
151 Set<String> dataformats = new HashSet<String>();
152 Set<String> dependsOn = new HashSet<String>();
153 for (RouteDefinition rd : ccfb.getContext().getRouteDefinitions()) {
154 findInputComponents(rd.getInputs(), components, languages, dataformats);
155 findOutputComponents(rd.getOutputs(), components, languages, dataformats);
156 }
157 try {
158 for (String component : components) {
159 ComponentMetadata cm = context.getComponentDefinitionRegistry().getComponentDefinition(".camelBlueprint.componentResolver." + component);
160 if (cm == null) {
161 MutableReferenceMetadata svc = context.createMetadata(MutableReferenceMetadata.class);
162 svc.setId(".camelBlueprint.componentResolver." + component);
163 svc.setFilter("(component=" + component + ")");
164 try {
165 // Try to set the runtime interface (only with aries blueprint > 0.1
166 svc.getClass().getMethod("setRuntimeInterface", Class.class).invoke(svc, ComponentResolver.class);
167 } catch (Throwable t) {
168 // Check if the bundle can see the class
169 try {
170 PassThroughMetadata ptm = (PassThroughMetadata) context.getComponentDefinitionRegistry().getComponentDefinition("blueprintBundle");
171 Bundle b = (Bundle) ptm.getObject();
172 if (b.loadClass(ComponentResolver.class.getName()) != ComponentResolver.class) {
173 throw new UnsupportedOperationException();
174 }
175 svc.setInterface(ComponentResolver.class.getName());
176 } catch (Throwable t2) {
177 throw new UnsupportedOperationException();
178 }
179 }
180 context.getComponentDefinitionRegistry().registerComponentDefinition(svc);
181 dependsOn.add(svc.getId());
182 }
183 }
184 for (String language : languages) {
185 ComponentMetadata cm = context.getComponentDefinitionRegistry().getComponentDefinition(".camelBlueprint.languageResolver." + language);
186 if (cm == null) {
187 MutableReferenceMetadata svc = context.createMetadata(MutableReferenceMetadata.class);
188 svc.setId(".camelBlueprint.languageResolver." + language);
189 svc.setFilter("(language=" + language + ")");
190 try {
191 // Try to set the runtime interface (only with aries blueprint > 0.1
192 svc.getClass().getMethod("setRuntimeInterface", Class.class).invoke(svc, LanguageResolver.class);
193 } catch (Throwable t) {
194 // Check if the bundle can see the class
195 try {
196 PassThroughMetadata ptm = (PassThroughMetadata) context.getComponentDefinitionRegistry().getComponentDefinition("blueprintBundle");
197 Bundle b = (Bundle) ptm.getObject();
198 if (b.loadClass(LanguageResolver.class.getName()) != LanguageResolver.class) {
199 throw new UnsupportedOperationException();
200 }
201 svc.setInterface(LanguageResolver.class.getName());
202 } catch (Throwable t2) {
203 throw new UnsupportedOperationException();
204 }
205 }
206 context.getComponentDefinitionRegistry().registerComponentDefinition(svc);
207 dependsOn.add(svc.getId());
208 }
209 }
210 for (String dataformat : dataformats) {
211 ComponentMetadata cm = context.getComponentDefinitionRegistry().getComponentDefinition(".camelBlueprint.dataformatResolver." + dataformat);
212 if (cm == null) {
213 MutableReferenceMetadata svc = context.createMetadata(MutableReferenceMetadata.class);
214 svc.setId(".camelBlueprint.dataformatResolver." + dataformat);
215 svc.setFilter("(dataformat=" + dataformat + ")");
216 try {
217 // Try to set the runtime interface (only with aries blueprint > 0.1
218 svc.getClass().getMethod("setRuntimeInterface", Class.class).invoke(svc, DataFormatResolver.class);
219 } catch (Throwable t) {
220 // Check if the bundle can see the class
221 try {
222 PassThroughMetadata ptm = (PassThroughMetadata) context.getComponentDefinitionRegistry().getComponentDefinition("blueprintBundle");
223 Bundle b = (Bundle) ptm.getObject();
224 if (b.loadClass(DataFormatResolver.class.getName()) != DataFormatResolver.class) {
225 throw new UnsupportedOperationException();
226 }
227 svc.setInterface(DataFormatResolver.class.getName());
228 } catch (Throwable t2) {
229 throw new UnsupportedOperationException();
230 }
231 }
232 context.getComponentDefinitionRegistry().registerComponentDefinition(svc);
233 dependsOn.add(svc.getId());
234 }
235 }
236 } catch (UnsupportedOperationException e) {
237 LOG.warn("Unable to add dependencies on to camel components OSGi services. "
238 + "The Apache Aries blueprint implementation used it too old and the blueprint bundle can not see the org.apache.camel.spi package.");
239 components.clear();
240 languages.clear();
241 dataformats.clear();
242 }
243
244 MutablePassThroughMetadata factory = context.createMetadata(MutablePassThroughMetadata.class);
245 factory.setId(".camelBlueprint.passThrough." + contextId);
246 factory.setObject(new PassThroughCallable<Object>(value));
247 factory.setDependsOn(new ArrayList<String>(components));
248
249 MutableBeanMetadata factory2 = context.createMetadata(MutableBeanMetadata.class);
250 factory2.setId(".camelBlueprint.factory." + contextId);
251 factory2.setFactoryComponent(factory);
252 factory2.setFactoryMethod("call");
253 factory2.setDestroyMethod("destroy");
254
255 MutableBeanMetadata ctx = context.createMetadata(MutableBeanMetadata.class);
256 ctx.setId(contextId);
257 ctx.setFactoryComponent(factory2);
258 ctx.setFactoryMethod("getContext");
259 ctx.setInitMethod("init");
260 ctx.setDestroyMethod("destroy");
261
262 return ctx;
263 }
264 return null;
265 }
266
267 private void findInputComponents(List<FromDefinition> defs, Set<String> components, Set<String> languages, Set<String> dataformats) {
268 if (defs != null) {
269 for (FromDefinition def : defs) {
270 findUriComponent(def.getUri(), components);
271 }
272 }
273 }
274
275 @SuppressWarnings("unchecked")
276 private void findOutputComponents(List<ProcessorDefinition> defs, Set<String> components, Set<String> languages, Set<String> dataformats) {
277 if (defs != null) {
278 for (ProcessorDefinition def : defs) {
279 if (def instanceof SendDefinition) {
280 findUriComponent(((SendDefinition) def).getUri(), components);
281 }
282 if (def instanceof MarshalDefinition) {
283 findDataFormat(((MarshalDefinition) def).getDataFormatType(), dataformats);
284 }
285 if (def instanceof UnmarshalDefinition) {
286 findDataFormat(((UnmarshalDefinition) def).getDataFormatType(), dataformats);
287 }
288 if (def instanceof ExpressionNode) {
289 findLanguage(((ExpressionNode) def).getExpression(), languages);
290 }
291 if (def instanceof ResequenceDefinition) {
292 findLanguage(((ResequenceDefinition) def).getExpressions(), languages);
293 }
294 if (def instanceof AggregateDefinition) {
295 findLanguage(((AggregateDefinition) def).getExpression(), languages);
296 findLanguage(((AggregateDefinition) def).getCorrelationExpression(), languages);
297 findLanguage(((AggregateDefinition) def).getCompletionPredicate(), languages);
298 findLanguage(((AggregateDefinition) def).getCompletionTimeoutExpression(), languages);
299 findLanguage(((AggregateDefinition) def).getCompletionSizeExpression(), languages);
300 }
301 if (def instanceof CatchDefinition) {
302 findLanguage(((CatchDefinition) def).getHandled(), languages);
303 }
304 if (def instanceof OnExceptionDefinition) {
305 findLanguage(((OnExceptionDefinition) def).getRetryWhile(), languages);
306 findLanguage(((OnExceptionDefinition) def).getHandled(), languages);
307 findLanguage(((OnExceptionDefinition) def).getContinued(), languages);
308 }
309 if (def instanceof SortDefinition) {
310 findLanguage(((SortDefinition) def).getExpression(), languages);
311 }
312 if (def instanceof WireTapDefinition) {
313 findLanguage(((WireTapDefinition) def).getNewExchangeExpression(), languages);
314 }
315 findOutputComponents(def.getOutputs(), components, languages, dataformats);
316 }
317 }
318 }
319
320 private void findLanguage(ExpressionDefinition expression, Set<String> languages) {
321 if (expression != null) {
322 String lang = expression.getLanguage();
323 if (lang != null && lang.length() > 0) {
324 languages.add(lang);
325 }
326 }
327 }
328
329 private void findLanguage(List<ExpressionDefinition> expressions, Set<String> languages) {
330 if (expressions != null) {
331 for (ExpressionDefinition e : expressions) {
332 findLanguage(e, languages);
333 }
334 }
335 }
336
337 private void findLanguage(ExpressionSubElementDefinition expression, Set<String> languages) {
338 if (expression != null) {
339 findLanguage(expression.getExpressionType(), languages);
340 }
341 }
342
343 private void findDataFormat(DataFormatDefinition dfd, Set<String> dataformats) {
344 if (dfd != null && dfd.getDataFormatName() != null) {
345 dataformats.add(dfd.getDataFormatName());
346 }
347 }
348
349 private void findUriComponent(String uri, Set<String> components) {
350 if (uri != null) {
351 String splitURI[] = ObjectHelper.splitOnCharacter(uri, ":", 2);
352 if (splitURI[1] != null) {
353 String scheme = splitURI[0];
354 components.add(scheme);
355 }
356 }
357 }
358
359 public ComponentMetadata decorate(Node node, ComponentMetadata component, ParserContext context) {
360 return null;
361 }
362
363 protected Object parseUsingJaxb(Element element, ParserContext parserContext, Binder<Node> binder) {
364 try {
365 return binder.unmarshal(element);
366 } catch (JAXBException e) {
367 throw new ComponentDefinitionException("Failed to parse JAXB element: " + e, e);
368 }
369 }
370
371 public JAXBContext getJaxbContext() throws JAXBException {
372 if (jaxbContext == null) {
373 jaxbContext = createJaxbContext();
374 }
375 return jaxbContext;
376 }
377
378 protected JAXBContext createJaxbContext() throws JAXBException {
379 StringBuilder packages = new StringBuilder();
380 for (Class cl : getJaxbPackages()) {
381 if (packages.length() > 0) {
382 packages.append(":");
383 }
384 packages.append(cl.getName().substring(0, cl.getName().lastIndexOf('.')));
385 }
386 return JAXBContext.newInstance(packages.toString(), getClass().getClassLoader());
387 }
388
389 protected Set<Class> getJaxbPackages() {
390 Set<Class> classes = new HashSet<Class>();
391 classes.add(CamelContextFactoryBean.class);
392 classes.add(AbstractCamelContextFactoryBean.class);
393 classes.add(org.apache.camel.ExchangePattern.class);
394 classes.add(org.apache.camel.model.RouteDefinition.class);
395 classes.add(org.apache.camel.model.config.StreamResequencerConfig.class);
396 classes.add(org.apache.camel.model.dataformat.DataFormatsDefinition.class);
397 classes.add(org.apache.camel.model.language.ExpressionDefinition.class);
398 classes.add(org.apache.camel.model.loadbalancer.RoundRobinLoadBalancerDefinition.class);
399 return classes;
400 }
401
402 public static class PassThroughCallable<T> implements Callable<T> {
403
404 private T value;
405
406 public PassThroughCallable(T value) {
407 this.value = value;
408 }
409
410 public T call() throws Exception {
411 return value;
412 }
413 }
414
415 }