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