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