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    }