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