/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF 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 org.apache.tuscany.sca.implementation.bpel.xml; import static javax.xml.stream.XMLStreamConstants.END_ELEMENT; import static javax.xml.stream.XMLStreamConstants.START_ELEMENT; import java.io.InputStream; import java.net.URI; import java.net.URL; import java.util.Iterator; import java.util.List; import javax.xml.namespace.QName; import javax.xml.stream.XMLInputFactory; import javax.xml.stream.XMLStreamReader; import org.apache.tuscany.sca.assembly.AssemblyFactory; import org.apache.tuscany.sca.assembly.Property; import org.apache.tuscany.sca.contribution.processor.BaseStAXArtifactProcessor; import org.apache.tuscany.sca.contribution.processor.ContributionReadException; import org.apache.tuscany.sca.contribution.processor.ContributionResolveException; import org.apache.tuscany.sca.contribution.processor.ProcessorContext; import org.apache.tuscany.sca.contribution.processor.URLArtifactProcessor; import org.apache.tuscany.sca.contribution.resolver.ModelResolver; import org.apache.tuscany.sca.core.FactoryExtensionPoint; import org.apache.tuscany.sca.implementation.bpel.BPELFactory; import org.apache.tuscany.sca.implementation.bpel.BPELProcessDefinition; import org.apache.tuscany.sca.interfacedef.wsdl.WSDLFactory; import org.apache.tuscany.sca.monitor.Monitor; import org.apache.tuscany.sca.monitor.Problem; import org.apache.tuscany.sca.monitor.Problem.Severity; /** * BPEL document processor responsible for reading a BPEL file and producing necessary model info about it * * Handles both BPEL 1.1 documents and BPEL 2.0 documents * @version $Rev$ $Date$ */ public class BPELDocumentProcessor extends BaseStAXArtifactProcessor implements URLArtifactProcessor<BPELProcessDefinition> { private final static XMLInputFactory inputFactory = XMLInputFactory.newInstance(); private final BPELFactory factory; private WSDLFactory WSDLfactory; private AssemblyFactory assemblyFactory; public BPELDocumentProcessor(FactoryExtensionPoint modelFactories) { this.factory = modelFactories.getFactory(BPELFactory.class); this.WSDLfactory = modelFactories.getFactory(WSDLFactory.class); this.assemblyFactory = modelFactories.getFactory(AssemblyFactory.class); } public String getArtifactType() { return "*.bpel"; } public Class<BPELProcessDefinition> getModelType() { return BPELProcessDefinition.class; } /** * Read the BPEL Process definition file from the location identified by an artifact URL * @param contributionURL - URL of the Contribution containing the Process definition * @param artifactURI - URI of the artifact containing the BPEL Process definition * @param artifactURL - URL of the artifact containing the BPEL Process definition * @return BPELProcessDefinition - SCA model of the BPEL Process */ public BPELProcessDefinition read(URL contributionURL, URI artifactURI, URL artifactURL, ProcessorContext context) throws ContributionReadException { BPELProcessDefinition processDefinition = null; try { processDefinition = readProcessDefinition(artifactURL, context.getMonitor()); processDefinition.setURI(artifactURI.toString()); processDefinition.setUnresolved(true); } catch (Exception e) { ContributionReadException ce = new ContributionReadException(e); error(context.getMonitor(), "ContributionReadException", artifactURL, ce); } return processDefinition; } public void resolve(BPELProcessDefinition model, ModelResolver resolver, ProcessorContext context) throws ContributionResolveException { // Delegate resolving to model resolver if (model != null || model.isUnresolved()) { resolver.resolveModel(BPELProcessDefinition.class, model, context); } } // end resolve /** * Read a process definition. * * @param doc * @return * @throws Exception */ private BPELProcessDefinition readProcessDefinition(URL doc, Monitor monitor) throws Exception { BPELProcessDefinition processDefinition = factory.createBPELProcessDefinition(); processDefinition.setUnresolved(true); processDefinition.setLocation(doc.toString()); InputStream is = doc.openStream(); XMLStreamReader reader = null; try { reader = inputFactory.createXMLStreamReader(is); /* * The principle here is to look for partnerLink elements, which * form either services or references. A partnerLink can be EITHER - * the algorithm for deciding is: * 1) Explicit marking with sca:reference or sca:service attribute * 2) "first use" of the partnerLink by specific BPEL activity elements: * <onEvent../>, <receive../> or <pick../> elements imply a service * <invoke../> implies a reference */ // TODO - need to handle <scope../> elements as kind of "nested" processes // - and scopes introduce the possibility of partnerLinks with the // same name at different levels of scope.... (yuk!!) boolean completed = false; while (!completed) { switch (reader.next()) { case START_ELEMENT: QName qname = reader.getName(); if (BPELProcessorConstants.PROCESS_ELEMENT.equals(qname) || BPELProcessorConstants.PROCESS_ELEMENT_20.equals(qname)) { QName processName = new QName(getString(reader, BPELProcessorConstants.TARGET_NAMESPACE), getString(reader, BPELProcessorConstants.NAME_ELEMENT)); processDefinition.setName(processName); } else if (BPELProcessorConstants.PARTNERLINK_ELEMENT.equals(qname) || BPELProcessorConstants.PARTNERLINK_ELEMENT_20.equals(qname)) { processDefinition.getPartnerLinks().add(processPartnerLinkElement(reader, monitor)); } else if (BPELProcessorConstants.ONEVENT_ELEMENT.equals(qname) || BPELProcessorConstants.RECEIVE_ELEMENT.equals(qname) || BPELProcessorConstants.ONMESSAGE_ELEMENT.equals(qname) || BPELProcessorConstants.ONEVENT_ELEMENT_20.equals(qname) || BPELProcessorConstants.RECEIVE_ELEMENT_20.equals(qname) || BPELProcessorConstants.ONMESSAGE_ELEMENT_20.equals(qname)) { processPartnerLinkAsService(reader.getAttributeValue(null, "partnerLink"), processDefinition.getPartnerLinks(), monitor); } else if (BPELProcessorConstants.INVOKE_ELEMENT.equals(qname) || BPELProcessorConstants.INVOKE_ELEMENT_20.equals(qname)) { processPartnerLinkAsReference(reader.getAttributeValue(null, "partnerLink"), processDefinition.getPartnerLinks(), monitor); } else if (BPELProcessorConstants.IMPORT_ELEMENT.equals(qname) || BPELProcessorConstants.IMPORT_ELEMENT_20.equals(qname)) { processDefinition.getImports().add(processImportElement(reader)); } else if (BPELProcessorConstants.VARIABLE_ELEMENT.equals(qname) || BPELProcessorConstants.VARIABLE_ELEMENT_20.equals(qname)) { // deal with variables that are SCA properties through the presence of a sca-bpel:property="yes" attribute Property aProperty = processVariableElement(reader); if( aProperty != null ) { processDefinition.getProperties().add( aProperty ); } // end if } // end if break; case END_ELEMENT: qname = reader.getName(); if (BPELProcessorConstants.PROCESS_ELEMENT.equals(qname) || BPELProcessorConstants.PROCESS_ELEMENT_20.equals(qname)) { completed = true; break; } // end if } // end switch } // end while } finally { if (reader != null) reader.close(); is.close(); } // end try return processDefinition; } // end readProcessDefinition /** * Processes a BPEL <variable/> element and creates an SCA <property/> element if the variable is * marked with sca-bpel:property="yes" * A BPEL <variable/> element can declare its type in one of 3 ways: * 1. using @type attribute, which must reference an XSD declared type * 2. using @element attribute, which must reference an XSD global element * 3. using @message attribute, which must reference a WSDL defined message type * @param reader - XMLStreamReader reading the BPEL process * @throws ContributionReadException */ private Property processVariableElement( XMLStreamReader reader) throws ContributionReadException { String scaProperty = reader.getAttributeValue(BPELProcessorConstants.SCA_BPEL_NS, "property"); if( "yes".equals(scaProperty)) { String varName = reader.getAttributeValue(null ,"name"); String varType = reader.getAttributeValue(null, "type"); String varElement = reader.getAttributeValue(null, "element"); String varMessage = reader.getAttributeValue(null, "message"); // Pass over this variable if there is no name, or if there is no type information if( varName == null ) return null; if( varType == null && varElement == null && varMessage == null ) return null; QName typeQName = getQNameValue( reader, varType ); QName elementQName = getQNameValue( reader, varElement ); // TODO deal with properties declared with @message for typing Property theProperty = assemblyFactory.createProperty(); theProperty.setName(varName); theProperty.setXSDType(typeQName); theProperty.setXSDElement(elementQName); return theProperty; } // end if return null; } // end processVariableElement /** * Processes a partnerLink element from the BPEL process and creates a * BPELPartnerLink object * * @param reader */ private BPELPartnerLinkElement processPartnerLinkElement(XMLStreamReader reader, Monitor monitor) throws ContributionReadException { BPELPartnerLinkElement partnerLink = new BPELPartnerLinkElement( reader.getAttributeValue(null, "name"), getQNameValue(reader, reader.getAttributeValue(null, "partnerLinkType")), reader.getAttributeValue(null, "myRole"), reader.getAttributeValue(null, "partnerRole")); // See if there are any SCA extension attributes String scaService = reader.getAttributeValue(BPELProcessorConstants.SCA_BPEL_NS, "service"); String scaReference = reader.getAttributeValue(BPELProcessorConstants.SCA_BPEL_NS, "reference"); if ((scaService != null) && (scaReference != null)) { // It is incorrect to set both service & reference attributes error(monitor, "PartnerLinkHasBothAttr", partnerLink, reader.getAttributeValue(null, "name")); throw new ContributionReadException("BPEL PartnerLink " + reader.getAttributeValue(null, "name") + " has both sca:reference and sca:service attributes set"); } // end if // Set the SCA type and the related name, if present if (scaService != null) partnerLink.setAsService(scaService); else if (scaReference != null) partnerLink.setAsReference(scaReference); return partnerLink; } // end processPartnerLinkElement /** * Processes an <import../> element from the BPEL process and creates a * BPELImportElement object * * @param reader */ private BPELImportElement processImportElement(XMLStreamReader reader) { return (new BPELImportElement(reader.getAttributeValue(null, "location"), reader.getAttributeValue(null, "importType"), reader.getAttributeValue(null, "namespace"))); } // end processImportElement /** * Mark a named partnerLink as a Service, unless it is already marked as a * Reference * * @param partnerLinkName * @param partnerLinks */ private void processPartnerLinkAsService(String partnerLinkName, List<BPELPartnerLinkElement> partnerLinks, Monitor monitor) { BPELPartnerLinkElement partnerLink = findPartnerLinkByName(partnerLinks, partnerLinkName); if (partnerLink == null) { warning(monitor, "ReferencePartnerLinkNotInList", partnerLinkName, partnerLinkName); } else { // Set the type of the partnerLink to "service" if not already // set... if (!partnerLink.isSCATyped()) partnerLink.setAsService(partnerLinkName); } // endif } // end processPartnerLinkAsReference /** * Mark a named partnerLink as a Reference, unless it is already marked as a * Service * * @param partnerLinkName * @param partnerLinks */ private void processPartnerLinkAsReference(String partnerLinkName, List<BPELPartnerLinkElement> partnerLinks, Monitor monitor) { BPELPartnerLinkElement partnerLink = findPartnerLinkByName(partnerLinks, partnerLinkName); if (partnerLink == null) { warning(monitor, "ReferencePartnerLinkNotInList", partnerLinkName, partnerLinkName); } else { // Set the type of the partnerLink to "service" if not already // set... if (!partnerLink.isSCATyped()) partnerLink.setAsReference(partnerLinkName); } // endif } // end processPartnerLinkAsReference /** * Finds a PartnerLink by name from a List of PartnerLinks returns null if * there is no partnerLink with a matching name - returns the PartnerLink * with a matching name * * @param partnerLinks * @param partnerLinkName */ private BPELPartnerLinkElement findPartnerLinkByName(List<BPELPartnerLinkElement> partnerLinks, String partnerLinkName) { // Scan the list looking for a partner link with the supplied name Iterator<BPELPartnerLinkElement> it = partnerLinks.iterator(); while (it.hasNext()) { BPELPartnerLinkElement thePartnerLink = it.next(); if (thePartnerLink.getName().equals(partnerLinkName)) return thePartnerLink; } return null; } // end method findPartnerLinkByName /** * Report a warning. * * @param problems * @param message * @param model */ private void warning(Monitor monitor, String message, Object model, Object... messageParameters) { if (monitor != null) { Problem problem = monitor.createProblem(this.getClass().getName(), "impl-bpel-validation-messages", Severity.WARNING, model, message, (Object[])messageParameters); monitor.problem(problem); } } /** * Report a error. * * @param problems * @param message * @param model */ private void error(Monitor monitor, String message, Object model, Object... messageParameters) { if (monitor != null) { Problem problem = monitor.createProblem(this.getClass().getName(), "impl-bpel-validation-messages", Severity.ERROR, model, message, (Object[])messageParameters); monitor.problem(problem); } } /** * Report a exception. * * @param problems * @param message * @param model */ private void error(Monitor monitor, String message, Object model, Exception ex) { if (monitor != null) { Problem problem = monitor.createProblem(this.getClass().getName(), "impl-bpel-validation-messages", Severity.ERROR, model, message, ex); monitor.problem(problem); } } }