/* * 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.spring; import java.io.File; import java.util.HashMap; import java.util.Map; 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.Channel; import com.github.lbroudoux.dsl.eip.CompositeProcessor; import com.github.lbroudoux.dsl.eip.ConditionalRoute; 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; import com.github.lbroudoux.dsl.eip.Router; import com.github.lbroudoux.dsl.eip.RoutingType; /** * Parser for Spring integration context file. Just build a new instance and call * <code>parseAndFillModel()</code> with already initialized model and it should go ! * @author laurent */ public class SpringIntegrationFileParser { private static final String SPRING_BEANS_NS = "http://www.springframework.org/schema/beans"; private static final String SPRING_INT_NS = "http://www.springframework.org/schema/integration"; private final File routeFile; private Map<String, Channel> channelsMap = new HashMap<String, Channel>(); private Map<String, Endpoint> endpointsMap = new HashMap<String, Endpoint>(); public SpringIntegrationFileParser(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(); // Check that it's really a Spring context file. if (!"beans".equals(root.getLocalName()) || !SPRING_BEANS_NS.equals(root.getNamespaceURI())) { throw new IllegalArgumentException("Given routeFile seems not to be a valid Spring context file"); } // Pre-requisite : we should have a Route object added to model. Route route = EipFactory.eINSTANCE.createRoute(); route.setName(routeFile.getName().substring(0, routeFile.getName().lastIndexOf('.'))); model.getOwnedRoutes().add(route); // First, extract and add channels to model. NodeList channels = root.getElementsByTagNameNS(SPRING_INT_NS, "channel"); for (int i=0; i<channels.getLength(); i++) { Node channelNode = channels.item(i); route.getOwnedChannels().add(createChannel(channelNode)); } channels = root.getElementsByTagNameNS(SPRING_INT_NS, "publish-subscribe-channel"); for (int i=0; i<channels.getLength(); i++) { Node channelNode = channels.item(i); createPubSubChannels(route, channelNode); } // Then, extract and add endpoints to model. NodeList children = root.getChildNodes(); parseAndFillEndpoints(children, route.getOwnedEndpoints()); } /** Parse children NodeList and fill the given endpoint collection. */ private void parseAndFillEndpoints(NodeList children, EList<Endpoint> endpoints) { for (int i=0; i<children.getLength(); i++) { Node childNode = children.item(i); if (childNode.getNodeType() == Node.ELEMENT_NODE && !"channel".equals(childNode.getLocalName())){ Endpoint endpoint = createEndpoint(childNode); // Prevent unknown null endpoint being inserted into no-null constrained list. if (endpoint != null) { endpoints.add(endpoint); // In case of composite, we should recurse. if (endpoint instanceof CompositeProcessor) { NodeList compositeChildren = childNode.getChildNodes(); parseAndFillEndpoints(compositeChildren, ((CompositeProcessor)endpoint).getOwnedEndpoints()); } } } } } /** Create Channel model element from node and its attributes. */ private Channel createChannel(Node channelNode) { Element channelElement = (Element) channelNode; Channel channel = EipFactory.eINSTANCE.createChannel(); channel.setName(channelElement.getAttribute("id")); channelsMap.put(channel.getName(), channel); return channel; } /** Create Channels mode elements from node and its attributes. */ private void createPubSubChannels(Route route, Node channelNode) { Element channelElement = (Element) channelNode; // TODO: retrieval and creation of multiple channels in case of PubSub semantic must be finalized. Channel channel = EipFactory.eINSTANCE.createChannel(); channel.setName(channelElement.getAttribute("id")); channelsMap.put(channel.getName(), channel); route.getOwnedChannels().add(channel); } /** Create Endpoint model element from node, its attributes and perhaps children. */ private Endpoint createEndpoint(Node endpointNode) { Element endpointElement = (Element) endpointNode; Endpoint endpoint = null; // Determine the correct implementation of Endpoint. if ("filter".equals(endpointElement.getLocalName())) { endpoint = EipFactory.eINSTANCE.createFilter(); } else if ("transformer".equals(endpointElement.getLocalName())) { endpoint = EipFactory.eINSTANCE.createTransformer(); } else if ("enricher".equals(endpointElement.getLocalName())) { endpoint = EipFactory.eINSTANCE.createEnricher(); } else if ("chain".equals(endpointElement.getLocalName())) { endpoint = EipFactory.eINSTANCE.createCompositeProcessor(); } else if ("splitter".equals(endpointElement.getLocalName())) { endpoint = EipFactory.eINSTANCE.createSplitter(); } else if ("aggregator".equals(endpointElement.getLocalName())) { endpoint = EipFactory.eINSTANCE.createAggregator(); } else if ("resequencer".equals(endpointElement.getLocalName())) { endpoint = EipFactory.eINSTANCE.createResequencer(); // Complete specific attributes of Resequencer. Resequencer resequencer = (Resequencer) endpoint; String strategy = endpointElement.getAttribute("correlation-strategy"); String expression = endpointElement.getAttribute("correlation-strategy-expression"); String streamSequences = endpointElement.getAttribute("release-partial-sequences"); if (strategy != null && strategy.length() > 0) { resequencer.setStrategy(strategy); } if (expression != null && expression.length() > 0) { resequencer.setExpression(expression); } if (streamSequences != null && streamSequences.length() > 0) { resequencer.setStreamSequences(Boolean.valueOf(streamSequences)); } } else if ("gateway".equals(endpointElement.getLocalName())) { endpoint = EipFactory.eINSTANCE.createGateway(); } else if ("service-activator".equals(endpointElement.getLocalName())) { endpoint = EipFactory.eINSTANCE.createServiceActivator(); } else if (endpointElement.getLocalName().contains("router")) { // Complete common attributes for Router. endpoint = EipFactory.eINSTANCE.createRouter(); Router router = ((Router) endpoint); String channelOut = endpointElement.getAttribute("default-output-channel"); if (channelOut != null && channelOut.trim().length() > 0) { router.getToChannels().add(channelsMap.get(channelOut)); } NodeList mappings = endpointElement.getElementsByTagNameNS(SPRING_INT_NS, "mapping"); // Add a conditional route for each mapping. for (int i=0; i<mappings.getLength(); i++) { Element mapping = (Element)mappings.item(i); ConditionalRoute cRoute = EipFactory.eINSTANCE.createConditionalRoute(); String cRouteChannel = mapping.getAttribute("channel"); cRoute.setChannel(channelsMap.get(cRouteChannel)); cRoute.setCondition(mapping.getAttribute("id")); router.getOwnedRoutes().add(cRoute); } // Complete specific router type attributes. if ("header-value-router".equals(endpointElement.getLocalName())) { router.setType(RoutingType.HEADER_VALUE); } else if ("payload-type-router".equals(endpointElement.getLocalName())) { router.setType(RoutingType.PAYLOAD_TYPE); } else if ("router".equals(endpointElement.getLocalName())) { router.setType(RoutingType.PAYLOAD_VALUE); } } // Complete Endpoint with common attributes if any. if (endpoint != null) { endpoint.setName(endpointElement.getAttribute("id")); String inputChannelName = endpointElement.getAttribute("input-channel"); if (inputChannelName != null && inputChannelName.trim().length() > 0) { Channel inputChannel = channelsMap.get(inputChannelName); // Prevent unknown null channel from being inserted in no-null constrained list. if (inputChannel != null) { endpoint.getFromChannels().add(inputChannel); } } String outputChannelName = endpointElement.getAttribute("output-channel"); if (outputChannelName != null && outputChannelName.trim().length() > 0) { endpoint.getToChannels().add(channelsMap.get(outputChannelName)); } endpointsMap.put(endpoint.getName(), endpoint); } return endpoint; } /** Parse the Spring integration 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); } /** Get a mandatory unique child from an element using its tag name. */ private static Node getUniqueChild(Element element, String tagName){ NodeList list = element.getElementsByTagName(tagName); assert list.getLength() == 1; return list.item(0); } /** Get an optional unique child from an element using its tag name. */ private static Node getOptionalChild(Element element, String tagName){ NodeList list = element.getElementsByTagName(tagName); if (list.getLength() > 0){ assert list.getLength() == 1; return list.item(0); } return null; } }