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.model;
018    
019    import java.io.ByteArrayInputStream;
020    import java.io.ByteArrayOutputStream;
021    import java.util.ArrayList;
022    import java.util.HashMap;
023    import java.util.Iterator;
024    import java.util.List;
025    import java.util.Map;
026    import javax.xml.bind.JAXBContext;
027    import javax.xml.bind.JAXBException;
028    import javax.xml.bind.Marshaller;
029    import javax.xml.bind.Unmarshaller;
030    
031    import org.apache.camel.CamelContext;
032    import org.apache.camel.model.language.NamespaceAwareExpression;
033    import org.apache.camel.util.CamelContextHelper;
034    import org.apache.camel.util.ObjectHelper;
035    
036    /**
037     * Helper for {@link RouteContextRefDefinition}.
038     */
039    public final class RouteContextRefDefinitionHelper {
040    
041        private static JAXBContext jaxbContext;
042    
043        private RouteContextRefDefinitionHelper() {
044        }
045    
046        /**
047         * Lookup the routes from the {@link RouteContextRefDefinition}.
048         * <p/>
049         * This implmementation must be used to lookup the routes as it performs a deep clone of the routes
050         * as a {@link RouteContextRefDefinition} can be re-used with multiple {@link CamelContext} and each
051         * context should have their own instances of the routes. This is to ensure no side-effects and sharing
052         * of instances between the contexts. For example such as property placeholders may be context specific
053         * so the routes should not use placeholders from another {@link CamelContext}.
054         *
055         * @param camelContext the CamelContext
056         * @param ref          the id of the {@link RouteContextRefDefinition} to lookup and get the routes.
057         * @return the routes.
058         */
059        @SuppressWarnings("unchecked")
060        public static synchronized List<RouteDefinition> lookupRoutes(CamelContext camelContext, String ref) {
061            ObjectHelper.notNull(camelContext, "camelContext");
062            ObjectHelper.notNull(ref, "ref");
063    
064            List<RouteDefinition> answer = CamelContextHelper.lookup(camelContext, ref, List.class);
065            if (answer == null) {
066                throw new IllegalArgumentException("Cannot find RouteContext with id " + ref);
067            }
068    
069            // must clone the route definitions as they can be reused with multiple CamelContexts
070            // and they would need their own instances of the definitions to not have side effects among
071            // the CamelContext - for example property placeholder resolutions etc.
072            List<RouteDefinition> clones = new ArrayList<RouteDefinition>(answer.size());
073            try {
074                JAXBContext jaxb = getOrCreateJAXBContext();
075                for (RouteDefinition def : answer) {
076                    RouteDefinition clone = cloneRouteDefinition(jaxb, def);
077                    if (clone != null) {
078                        clones.add(clone);
079                    }
080                }
081            } catch (Exception e) {
082                throw ObjectHelper.wrapRuntimeCamelException(e);
083            }
084    
085            return clones;
086        }
087    
088        private static synchronized JAXBContext getOrCreateJAXBContext() throws JAXBException {
089            if (jaxbContext == null) {
090                // must use classloader from CamelContext to have JAXB working
091                jaxbContext = JAXBContext.newInstance(Constants.JAXB_CONTEXT_PACKAGES, CamelContext.class.getClassLoader());
092            }
093            return jaxbContext;
094        }
095    
096        private static RouteDefinition cloneRouteDefinition(JAXBContext jaxbContext, RouteDefinition def) throws JAXBException {
097            Marshaller marshal = jaxbContext.createMarshaller();
098            ByteArrayOutputStream bos = new ByteArrayOutputStream();
099            marshal.marshal(def, bos);
100    
101            ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
102            Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
103            Object clone = unmarshaller.unmarshal(bis);
104    
105            if (clone != null && clone instanceof RouteDefinition) {
106                RouteDefinition def2 = (RouteDefinition) clone;
107    
108                // need to clone the namespaces also as they are not JAXB marshalled (as they are transient)
109                Iterator<ExpressionNode> it = ProcessorDefinitionHelper.filterTypeInOutputs(def.getOutputs(), ExpressionNode.class);
110                Iterator<ExpressionNode> it2 = ProcessorDefinitionHelper.filterTypeInOutputs(def2.getOutputs(), ExpressionNode.class);
111                while (it.hasNext() && it2.hasNext()) {
112                    ExpressionNode node = it.next();
113                    ExpressionNode node2 = it2.next();
114    
115                    NamespaceAwareExpression name = null;
116                    NamespaceAwareExpression name2 = null;
117                    if (node.getExpression() instanceof NamespaceAwareExpression) {
118                        name = (NamespaceAwareExpression) node.getExpression();
119                    }
120                    if (node2.getExpression() instanceof NamespaceAwareExpression) {
121                        name2 = (NamespaceAwareExpression) node2.getExpression();
122                    }
123    
124                    if (name != null && name2 != null && name.getNamespaces() != null && !name.getNamespaces().isEmpty()) {
125                        Map<String, String> map = new HashMap<String, String>();
126                        map.putAll(name.getNamespaces());
127                        name2.setNamespaces(map);
128                    }
129                }
130    
131                return def2;
132            }
133    
134            return null;
135        }
136    
137    }