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