/* * Licensed to Laurent Broudoux (the "Author") under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. Author licenses this * file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package com.github.lbroudoux.dsl.eip.parser.camel; import java.io.File; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.eclipse.emf.common.util.EList; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import com.github.lbroudoux.dsl.eip.Aggregator; import com.github.lbroudoux.dsl.eip.Channel; import com.github.lbroudoux.dsl.eip.CompositeProcessor; import com.github.lbroudoux.dsl.eip.EIPModel; import com.github.lbroudoux.dsl.eip.EipFactory; import com.github.lbroudoux.dsl.eip.Endpoint; import com.github.lbroudoux.dsl.eip.Resequencer; import com.github.lbroudoux.dsl.eip.Route; /** * Parser for Apache Camel Xml route file. Just build a new instance and call * <code>parseAndFillModel()</code> with already initialized model and it should go ! * @author laurent */ public class CamelXmlFileParser { private final File routeFile; private Aggregator currentAggregator = null; public CamelXmlFileParser(File routeFile) { this.routeFile = routeFile; } /** * Parse the routeFile given while building the instance and fill the model. * @param model The EIP Model to fill with parsed elements from routeFile. * @throws InvalidArgumentException if given file is not a valid Spring integration file */ public void parseAndFillModel(EIPModel model) throws Exception { // Parse and get root element. Document document = parseRouteFile(); Element root = document.getDocumentElement(); NodeList routes = root.getElementsByTagName("route"); Route route = EipFactory.eINSTANCE.createRoute(); for (int i=0; i<routes.getLength(); i++) { Node routeNode = routes.item(i); Element routeElement = (Element)routeNode; // Only main routes have an id. That's a constraint to make difference between main route // in the designer and sub-routes that results from publish/subscribe/multicast diffusion branches. if (routeElement.hasAttribute("id")) { route.setName(routeElement.getAttribute("id")); model.getOwnedRoutes().add(route); } // Then extract and recursively add endpoints and channels to model. Node child = routeNode.getFirstChild(); parseAndFillEndpoint(child, null, route.getOwnedEndpoints(), route.getOwnedChannels()); } } /** Parse given Node and fill and fill the given endpoint and channel collections. */ private void parseAndFillEndpoint(Node endpointNode, Channel incomingChannel, EList<Endpoint> endpoints, EList<Channel> channels) { if (endpointNode != null) { if (endpointNode.getNodeType() == Node.ELEMENT_NODE) { // Add incomingChannel only if we really have an endpoint. if (incomingChannel != null) { channels.add(incomingChannel); } Element endpointElement = (Element) endpointNode; Endpoint endpoint = null; boolean inspectChildren = false; // Determine the correct implementation of Endpoint. if ("from".equals(endpointElement.getLocalName())) { // We may have some different stuffs here ! Check uri in order to guess... String uri = endpointElement.getAttribute("uri"); if (uri.startsWith("direct:")) { // That's a multicast sub-route definition, use it to retrieve previously created // channel and place it as the current incomingChannel. String id = endpointElement.getAttribute("id"); incomingChannel = retrieveChannelByName(id, channels); } else { endpoint = EipFactory.eINSTANCE.createGateway(); } } else if ("transform".equals(endpointElement.getLocalName())) { endpoint = EipFactory.eINSTANCE.createTransformer(); } else if ("choice".equals(endpointElement.getLocalName())) { endpoint = EipFactory.eINSTANCE.createRouter(); inspectChildren = true; } else if ("filter".equals(endpointElement.getLocalName())) { endpoint = EipFactory.eINSTANCE.createFilter(); inspectChildren = true; } else if ("pipeline".equals(endpointElement.getLocalName())) { endpoint = EipFactory.eINSTANCE.createCompositeProcessor(); inspectChildren = true; } else if ("split".equals(endpointElement.getLocalName())) { endpoint = EipFactory.eINSTANCE.createSplitter(); inspectChildren = true; // We may have an aggregator referenced here. String strategyRef = endpointElement.getAttribute("strategyRef"); if (strategyRef != null && strategyRef.length() > 0) { String id = endpointElement.getAttribute("id"); String name = id.substring(0, id.indexOf('|')); String outgoingChannelName = id.substring(id.indexOf('|') + 1); Aggregator aggregator = EipFactory.eINSTANCE.createAggregator(); aggregator.setName(name); aggregator.setStrategy(strategyRef); Channel outgoingChannel = retrieveChannelByName(outgoingChannelName, channels); if (outgoingChannel == null) { outgoingChannel = EipFactory.eINSTANCE.createChannel(); outgoingChannel.setName(outgoingChannelName); channels.add(outgoingChannel); } aggregator.getToChannels().add(outgoingChannel); currentAggregator = aggregator; } } else if ("when".equals(endpointElement.getLocalName())) { inspectChildren = true; } else if ("otherwise".equals(endpointElement.getLocalName())) { inspectChildren = true; } else if ("multicast".equals(endpointElement.getLocalName())) { inspectChildren = true; // We may have an aggregator referenced here. String strategyRef = endpointElement.getAttribute("strategyRef"); if (strategyRef != null && strategyRef.length() > 0) { String id = endpointElement.getAttribute("id"); String name = id.substring(0, id.indexOf('|')); String outgoingChannelName = id.substring(id.indexOf('|') + 1); Aggregator aggregator = EipFactory.eINSTANCE.createAggregator(); aggregator.setName(name); aggregator.setStrategy(strategyRef); Channel outgoingChannel = retrieveChannelByName(outgoingChannelName, channels); if (outgoingChannel == null) { outgoingChannel = EipFactory.eINSTANCE.createChannel(); outgoingChannel.setName(outgoingChannelName); channels.add(outgoingChannel); } aggregator.getToChannels().add(outgoingChannel); currentAggregator = aggregator; } } else if ("resequence".equals(endpointElement.getLocalName())) { endpoint = EipFactory.eINSTANCE.createResequencer(); inspectChildren = true; } else if ("stream-config".equals(endpointElement.getLocalName())) { // Parent should be a Resequencer. Endpoint lastEndpoint = endpoints.get(endpoints.size() - 1); if (lastEndpoint instanceof Resequencer) { ((Resequencer) lastEndpoint).setStreamSequences(true); } } else if ("to".equals(endpointElement.getLocalName())) { // We may have a lot of stuffs here ! Check uri in order to guess... String uri = endpointElement.getAttribute("uri"); if (uri.startsWith("xslt:")) { endpoint = EipFactory.eINSTANCE.createTransformer(); } else if (uri.startsWith("switchyard:")) { endpoint = EipFactory.eINSTANCE.createServiceActivator(); } else if (uri.startsWith("direct:")) { // That's a multicast channel to a sub-route... String id = endpointElement.getAttribute("id"); Channel multicast = retrieveChannelByName(id, channels); if (multicast == null) { multicast = EipFactory.eINSTANCE.createChannel(); multicast.setName(endpointElement.getAttribute("id")); System.err.println("Adding mc channel with name: " + multicast.getName()); channels.add(multicast); } Endpoint lastEndpoint = endpoints.get(endpoints.size() - 1); lastEndpoint.getToChannels().add(multicast); // We cannot link channel to endpoint because it does not exist yet... // We cannot put channel as incomingChannel because next endpoint will // not be the outgoing endpoint... Incoming channel should be found when // dealing with sub-route endpoint! } } else { System.err.println("Got an unsupported: " + endpointElement.getLocalName()); } // Complete Endpoint with common attributes if any and store it. if (endpoint != null) { String id = endpointElement.getAttribute("id"); String endpointName = id; String outgoingChannelName = null; // Id may have "<endpoint_name>|<outgoing_channel_name>" format. if (id != null && id.contains("|")) { endpointName = id.substring(0, id.indexOf('|')); outgoingChannelName = id.substring(id.indexOf('|') + 1); } endpoint.setName(endpointName); endpoints.add(endpoint); // Associate with incoming channel if any. if (incomingChannel != null) { incomingChannel.setToEndpoint(endpoint); } // We have created an endpoint so we need an outgoingChannel that will become // incoming one for next endpoint to create ! But only if there's other endpoint to link to ! incomingChannel = EipFactory.eINSTANCE.createChannel(); incomingChannel.setFromEndpoint(endpoint); if (outgoingChannelName != null) { // Because we have a name, we're sure channel exists. We can add it here. incomingChannel.setName(outgoingChannelName); channels.add(incomingChannel); } } // If node may contain endpoint nodes, go deeper... if (inspectChildren) { Node firstChild = endpointNode.getFirstChild(); // In the CompositeProcessor case, we need to switch context for storing // children endpoints directly into endpoint owned ones. if (endpoint instanceof CompositeProcessor) { parseAndFillEndpoint(firstChild, incomingChannel, ((CompositeProcessor)endpoint).getOwnedEndpoints(), channels); } else { parseAndFillEndpoint(firstChild, incomingChannel, endpoints, channels); } // If we have an aggregator and were inspecting children of specific element, // we have now finished and must add it to the list of route endpoints. if (currentAggregator != null && ( "multicast".equals(endpointElement.getLocalName()) || "split".equals(endpointElement.getLocalName())) ) { endpoints.add(currentAggregator); currentAggregator = null; } } } // Pass to the next node if any... if (endpointNode.getNextSibling() != null) { parseAndFillEndpoint(endpointNode.getNextSibling(), incomingChannel, endpoints, channels); } } } /** Browse the list of channels for retrieving the one having specified name. */ private Channel retrieveChannelByName(String name, EList<Channel> channels) { for (Channel channel : channels) { if (channel.getName().equals(name)) { return channel; } } return null; } /** Parse the Apache Camel route file and return DOM Document. */ private Document parseRouteFile() throws Exception { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setNamespaceAware(true); DocumentBuilder builder = factory.newDocumentBuilder(); return builder.parse(routeFile); } }