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.spring.handler;
018
019 import java.util.HashSet;
020 import java.util.Set;
021
022 import javax.xml.bind.JAXBContext;
023 import javax.xml.bind.JAXBException;
024 import javax.xml.bind.Unmarshaller;
025
026 import org.w3c.dom.Element;
027 import org.w3c.dom.Node;
028 import org.w3c.dom.NodeList;
029
030 import org.apache.camel.builder.xml.XPathBuilder;
031 import org.apache.camel.spring.CamelBeanPostProcessor;
032 import org.apache.camel.spring.CamelContextFactoryBean;
033 import org.apache.camel.spring.EndpointFactoryBean;
034 import org.apache.camel.spring.remoting.CamelProxyFactoryBean;
035 import org.apache.camel.spring.remoting.CamelServiceExporter;
036 import org.apache.camel.util.ObjectHelper;
037
038 import org.springframework.beans.factory.BeanDefinitionStoreException;
039 import org.springframework.beans.factory.config.BeanDefinition;
040 import org.springframework.beans.factory.config.RuntimeBeanReference;
041 import org.springframework.beans.factory.parsing.BeanComponentDefinition;
042 import org.springframework.beans.factory.support.BeanDefinitionBuilder;
043 import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
044 import org.springframework.beans.factory.xml.ParserContext;
045 import org.springframework.util.xml.DomUtils;
046
047 import static org.apache.camel.util.ObjectHelper.isNotNullAndNonEmpty;
048
049 public class CamelNamespaceHandler extends NamespaceHandlerSupport {
050 public static final String JAXB_PACKAGES = "org.apache.camel.spring:org.apache.camel.model:org.apache.camel.model.language";
051
052 protected BeanDefinitionParser endpointParser = new BeanDefinitionParser(EndpointFactoryBean.class);
053 protected BeanDefinitionParser proxyParser = new BeanDefinitionParser(CamelProxyFactoryBean.class);
054 protected BeanDefinitionParser exportParser = new BeanDefinitionParser(CamelServiceExporter.class);
055 protected BeanDefinitionParser beanPostProcessorParser = new BeanDefinitionParser(CamelBeanPostProcessor.class);
056
057 protected Set<String> parserElementNames = new HashSet<String>();
058 private JAXBContext jaxbContext;
059
060 public void init() {
061 registerParser("endpoint", endpointParser);
062 registerParser("proxy", proxyParser);
063 registerParser("export", exportParser);
064
065 registerParser("camelContext", new BeanDefinitionParser(CamelContextFactoryBean.class) {
066 @Override
067 protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
068 super.doParse(element, parserContext, builder);
069
070 String contextId = element.getAttribute("id");
071
072 // lets avoid folks having to explicitly give an ID to a camel
073 // context
074 if (ObjectHelper.isNullOrBlank(contextId)) {
075 contextId = "camelContext";
076 element.setAttribute("id", contextId);
077 }
078
079 // now lets parse the routes
080 Object value = parseUsingJaxb(element, parserContext);
081 if (value instanceof CamelContextFactoryBean) {
082 CamelContextFactoryBean factoryBean = (CamelContextFactoryBean)value;
083 builder.addPropertyValue("routes", factoryBean.getRoutes());
084
085 if (factoryBean.getPackages().length > 0) {
086 builder.addPropertyValue("packages", factoryBean.getPackages());
087 }
088 }
089
090 boolean createdBeanPostProcessor = false;
091 NodeList list = element.getChildNodes();
092 int size = list.getLength();
093 for (int i = 0; i < size; i++) {
094 Node child = list.item(i);
095 if (child instanceof Element) {
096 Element childElement = (Element)child;
097 String localName = child.getLocalName();
098 if (localName.equals("beanPostProcessor")) {
099 createBeanPostProcessor(parserContext, contextId, childElement);
100 createdBeanPostProcessor = true;
101 } else if (localName.equals("endpoint")) {
102 BeanDefinition definition = endpointParser.parse(childElement, parserContext);
103 String id = childElement.getAttribute("id");
104 if (isNotNullAndNonEmpty(id)) {
105 // TODO we can zap this?
106 definition.getPropertyValues().addPropertyValue("context", new RuntimeBeanReference(contextId));
107 // definition.getPropertyValues().addPropertyValue("context",
108 // builder.getBeanDefinition());
109 parserContext.registerComponent(new BeanComponentDefinition(definition, id));
110 }
111 } else if (localName.equals("proxy")) {
112 BeanDefinition definition = proxyParser.parse(childElement, parserContext);
113 String id = childElement.getAttribute("id");
114 if (isNotNullAndNonEmpty(id)) {
115 parserContext.registerComponent(new BeanComponentDefinition(definition, id));
116 }
117 } else if (localName.equals("export")) {
118 BeanDefinition definition = exportParser.parse(childElement, parserContext);
119 String id = childElement.getAttribute("id");
120 if (isNotNullAndNonEmpty(id)) {
121 parserContext.registerComponent(new BeanComponentDefinition(definition, id));
122 }
123 }
124 }
125 }
126 if (!createdBeanPostProcessor) {
127 // no bean processor element so lets add a fake one
128 Element childElement = element.getOwnerDocument().createElement("beanPostProcessor");
129 element.appendChild(childElement);
130 createBeanPostProcessor(parserContext, contextId, childElement);
131 }
132 }
133 });
134
135 registerParser("xpath", new BeanDefinitionParser(XPathBuilder.class) {
136 @Override
137 protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
138 // lets create a child context
139 String xpath = DomUtils.getTextValue(element);
140 builder.addConstructorArg(xpath);
141 super.doParse(element, parserContext, builder);
142 builder.addPropertyValue("namespacesFromDom", element);
143 }
144 });
145 }
146
147 protected void createBeanPostProcessor(ParserContext parserContext, String contextId, Element childElement) {
148 String beanPostProcessorId = contextId + ":beanPostProcessor";
149 childElement.setAttribute("id", beanPostProcessorId);
150 BeanDefinition definition = beanPostProcessorParser.parse(childElement, parserContext);
151 definition.getPropertyValues().addPropertyValue("camelContext", new RuntimeBeanReference(contextId));
152 }
153
154 protected void registerScriptParser(String elementName, String engineName) {
155 registerParser(elementName, new ScriptDefinitionParser(engineName));
156 }
157
158 protected void registerParser(String name, org.springframework.beans.factory.xml.BeanDefinitionParser parser) {
159 parserElementNames.add(name);
160 registerBeanDefinitionParser(name, parser);
161 }
162
163 public Set<String> getParserElementNames() {
164 return parserElementNames;
165 }
166
167 protected Object parseUsingJaxb(Element element, ParserContext parserContext) {
168 try {
169 Unmarshaller unmarshaller = getJaxbContext().createUnmarshaller();
170 return unmarshaller.unmarshal(element);
171 } catch (JAXBException e) {
172 throw new BeanDefinitionStoreException("Failed to parse JAXB element: " + e, e);
173 }
174 }
175
176 protected JAXBContext getJaxbContext() throws JAXBException {
177 if (jaxbContext == null) {
178 jaxbContext = JAXBContext.newInstance(JAXB_PACKAGES);
179 }
180 return jaxbContext;
181 }
182 }