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