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 }