/** * Copyright 2007-2010 非也 * All rights reserved. * * This library is free software; you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License v3 as published by the Free Software * Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along * with this library; if not, see http://www.gnu.org/licenses/lgpl.html. * */ package org.fireflow.pdl.fpdl20.enginemodules.webservice; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.wsdl.Binding; import javax.wsdl.BindingInput; import javax.wsdl.BindingOperation; import javax.wsdl.BindingOutput; import javax.wsdl.Definition; import javax.wsdl.Input; import javax.wsdl.Message; import javax.wsdl.Operation; import javax.wsdl.OperationType; import javax.wsdl.Output; import javax.wsdl.Part; import javax.wsdl.Port; import javax.wsdl.PortType; import javax.wsdl.Service; import javax.wsdl.Types; import javax.wsdl.WSDLException; import javax.wsdl.extensions.soap.SOAPAddress; import javax.wsdl.extensions.soap.SOAPBinding; import javax.wsdl.extensions.soap.SOAPBody; import javax.wsdl.factory.WSDLFactory; import javax.wsdl.xml.WSDLWriter; import javax.xml.namespace.QName; import javax.xml.transform.Source; import javax.xml.transform.stream.StreamSource; import javax.xml.ws.Endpoint; import org.apache.ws.commons.schema.XmlSchema; import org.apache.ws.commons.schema.XmlSchemaCollection; import org.apache.ws.commons.schema.XmlSchemaSerializer.XmlSchemaSerializerException; import org.fireflow.client.WorkflowQuery; import org.fireflow.client.WorkflowSession; import org.fireflow.client.WorkflowSessionFactory; import org.fireflow.client.query.Restrictions; import org.fireflow.engine.context.AbsEngineModule; import org.fireflow.engine.context.RuntimeContext; import org.fireflow.engine.entity.repository.ProcessDescriptor; import org.fireflow.engine.entity.repository.ProcessDescriptorProperty; import org.fireflow.engine.entity.repository.ProcessKey; import org.fireflow.engine.entity.repository.ProcessRepository; import org.fireflow.engine.exception.EngineException; import org.fireflow.engine.exception.WebservicePublishException; import org.fireflow.engine.modules.environment.Environment; import org.fireflow.engine.modules.ousystem.impl.FireWorkflowSystem; import org.fireflow.engine.modules.persistence.PersistenceService; import org.fireflow.engine.modules.persistence.ProcessPersister; import org.fireflow.engine.modules.webservice.WebServiceManager; import org.fireflow.model.InvalidModelException; import org.fireflow.model.binding.ServiceBinding; import org.fireflow.model.servicedef.InterfaceDef; import org.fireflow.model.servicedef.OperationDef; import org.fireflow.model.servicedef.ServiceDef; import org.fireflow.model.servicedef.impl.CommonInterfaceDef; import org.fireflow.pdl.fpdl20.misc.FpdlConstants; import org.fireflow.pdl.fpdl20.process.Activity; import org.fireflow.pdl.fpdl20.process.Node; import org.fireflow.pdl.fpdl20.process.StartNode; import org.fireflow.pdl.fpdl20.process.SubProcess; import org.fireflow.pdl.fpdl20.process.WorkflowProcess; import org.fireflow.pdl.fpdl20.process.features.Feature; import org.fireflow.pdl.fpdl20.process.features.startnode.WebserviceStartFeature; import org.fireflow.server.WorkflowEngineService; import org.fireflow.service.callback.CallbackService; import org.fireflow.service.callback.ProcessServiceProvider; import org.firesoa.common.schema.NameSpaces; import org.firesoa.common.schema.WSDLConstants; import org.springframework.transaction.support.TransactionTemplate; import org.w3c.dom.Document; import com.ibm.wsdl.extensions.schema.SchemaImpl; import com.ibm.wsdl.extensions.soap.SOAPAddressImpl; import com.ibm.wsdl.extensions.soap.SOAPBindingImpl; import com.ibm.wsdl.extensions.soap.SOAPBodyImpl; /** * * @author 非也 nychen2000@163.com Fire Workflow 官方网站:www.firesoa.com 或者 * www.fireflow.org * */ public class WebServiceManagerFpdl20Impl extends AbsEngineModule implements WebServiceManager { RuntimeContext runtimeContext = null; boolean isInit = false; TransactionTemplate transactionTemplate = null; Map<QName,Boolean> servicePublishRegistry = new HashMap<QName,Boolean>(); public void init(RuntimeContext runtimeContext)throws EngineException{ if (isInit) return; try { this.publishAllCallbackServices(); if (runtimeContext.isPublishWorkflowService()){ publishFireWorkflowServer(); } } catch (WebservicePublishException e) { throw new EngineException(e); } isInit = true; } /** * @return the transactionTemplate */ public TransactionTemplate getTransactionTemplate() { return transactionTemplate; } /** * @param transactionTemplate the transactionTemplate to set */ public void setTransactionTemplate(TransactionTemplate transactionTemplate) { this.transactionTemplate = transactionTemplate; } /* * (non-Javadoc) * * @see * org.fireflow.engine.context.RuntimeContextAware#setRuntimeContext(org * .fireflow.engine.context.RuntimeContext) */ public void setRuntimeContext(RuntimeContext ctx) { runtimeContext = ctx; } /* * (non-Javadoc) * * @see org.fireflow.engine.context.RuntimeContextAware#getRuntimeContext() */ public RuntimeContext getRuntimeContext() { return runtimeContext; } /* * (non-Javadoc) * * @see org.fireflow.engine.modules.webservice.WebServiceManager# * publishAllCallbackServices() */ public void publishAllCallbackServices() throws WebservicePublishException{ // 1、查询所有的WorkflowProcess找到其中的Callback定义 WorkflowSession session = WorkflowSessionFactory.createWorkflowSession( runtimeContext, FireWorkflowSystem.getInstance()); WorkflowQuery<ProcessDescriptor> processDescQuery = session .createWorkflowQuery(ProcessDescriptor.class); // 未发布的也应该查询出来,因为已经运行的实例,即便流程未发布,也要响应webservice请求。 processDescQuery.add(Restrictions.eq( ProcessDescriptorProperty.HAS_CALLBACK_SERVICE, Boolean.TRUE)); List<ProcessDescriptor> descriptorList = processDescQuery.list(); if (descriptorList == null && descriptorList.size() == 0) return; PersistenceService persistenceService = runtimeContext.getEngineModule( PersistenceService.class, FpdlConstants.PROCESS_TYPE_FPDL20); ProcessPersister processPersister = persistenceService .getProcessPersister(); // 2、针对每个流程的Callback,发布Webservice for (ProcessDescriptor descriptor : descriptorList) { try { ProcessRepository repository = processPersister .findProcessRepositoryByProcessKey(new ProcessKey( descriptor.getProcessId(), descriptor .getVersion(), descriptor .getProcessType())); publishCallbackService((WorkflowProcess) repository .getProcessObject()); } catch (InvalidModelException ex) { // TODO 记录操作日志 ex.printStackTrace(); } } } protected void publishCallbackService(WorkflowProcess workflowProcess)throws WebservicePublishException { List<SubProcess> subflowList = workflowProcess.getLocalSubProcesses(); if (subflowList == null || subflowList.size() == 0) return; for (SubProcess subflow : subflowList) { // 首先检查Activity是否绑定了Callback service List<Activity> activityList = subflow.getActivities(); if (activityList != null) { for (Activity activity : activityList) { ServiceBinding svcBinding = activity.getServiceBinding(); if (svcBinding != null) { ServiceDef svcDef = workflowProcess.getService(svcBinding.getServiceId()); if (svcDef != null && svcDef instanceof CallbackService) { publishCallbackService(workflowProcess, subflow, activity, (CallbackService) svcDef); } } } } // 然后检查main subflow 的StartNode是否绑定了CallbackService if (WorkflowProcess.MAIN_PROCESS_NAME.equals(subflow.getName())) { List<StartNode> startNodeList = subflow.getStartNodes(); if (startNodeList != null) { for (StartNode startNode : startNodeList) { Feature ft = startNode.getFeature(); if (ft != null && ft instanceof WebserviceStartFeature) { WebserviceStartFeature wsFt = (WebserviceStartFeature) ft; ServiceBinding svcBinding = wsFt.getServiceBinding(); if (svcBinding != null) { ServiceDef svcDef = workflowProcess.getService(svcBinding.getServiceId()); if (svcDef != null && svcDef instanceof CallbackService) { publishCallbackService(workflowProcess, subflow, startNode, (CallbackService) svcDef); } } } } } } } } protected void publishCallbackService(WorkflowProcess workflowProcess, SubProcess subflow, Node node, CallbackService callbackService) throws WebservicePublishException{ CommonInterfaceDef commonInterfaceDef = (CommonInterfaceDef) callbackService .getInterface(); String serviceName = callbackService.getName()+"_"+callbackService.getVersion(); QName serviceQName = new QName(callbackService.getTargetNamespaceUri(), serviceName); if (servicePublishRegistry.get(serviceQName)!=null && servicePublishRegistry.get(serviceQName)){ return;//已经发布,不必重复发布; } QName portQName = new QName(callbackService.getTargetNamespaceUri(), commonInterfaceDef.getName()+"_Port");// // 1、构造address,wsdlfilename // TODO 如何组织address,使之与已有的Server context适应? Environment evn = runtimeContext .getDefaultEngineModule(Environment.class); String contextPath = evn.getWebserviceContextPath(); if (!contextPath.startsWith("/")){ contextPath = "/"+contextPath; } String address = "http://"+evn.getWebserviceIP()+":" +Integer.toString(evn.getWebservicePort()) +contextPath; // 2、构造wsdl List<Source> wsdls = null; try { wsdls = this .buildWSDL(callbackService, commonInterfaceDef,address,evn.getWorkDir(),serviceQName,portQName); } catch (FileNotFoundException e) { throw new WebservicePublishException(e); } catch (WSDLException e) { throw new WebservicePublishException(e); }catch(IOException e){ throw new WebservicePublishException(e); } // 3、指定service name 和port name Map<String, Object> properties = new HashMap<String, Object>(); properties.put(Endpoint.WSDL_SERVICE, serviceQName); properties.put(Endpoint.WSDL_PORT, portQName); // 4、构建Implementor ProcessServiceProvider implementor = new ProcessServiceProvider(); implementor.setWorkflowRuntimeContext(runtimeContext); implementor.setProcessId(workflowProcess.getId()); implementor.setProcessType(FpdlConstants.PROCESS_TYPE_FPDL20); if (node instanceof Activity){ Activity activity = (Activity)node; ServiceBinding binding = activity.getServiceBinding(); implementor.setServiceBinding(binding); }else if (node instanceof StartNode){ implementor.setStartNewProcess(true); WebserviceStartFeature wsFt = (WebserviceStartFeature)((StartNode)node).getFeature(); ServiceBinding svcBinding = wsFt.getServiceBinding(); implementor.setServiceBinding(svcBinding); } implementor.setCallbackService(callbackService); implementor.setTransactionTemplate(this.transactionTemplate); // 5、发布服务 Endpoint endpoint = Endpoint.create(implementor); endpoint.setMetadata(wsdls); endpoint.setProperties(properties); endpoint.publish(address); servicePublishRegistry.put(serviceQName, Boolean.TRUE); } /* (non-Javadoc) * @see org.fireflow.engine.modules.webservice.WebServiceManager#publishFireWorkflowServer() */ public void publishFireWorkflowServer() throws WebservicePublishException { Boolean b = (Boolean)servicePublishRegistry.get(WorkflowServer.SERVICE_QNAME); if (b!=null && b)return;//已经发布 Environment evn = runtimeContext .getDefaultEngineModule(Environment.class); String contextPath = evn.getWebserviceContextPath(); if (!contextPath.startsWith("/")){ contextPath = "/"+contextPath; } String address = "http://"+evn.getWebserviceIP()+":" +Integer.toString(evn.getWebservicePort()) +contextPath; WorkflowServer implementor = runtimeContext.getDefaultEngineModule(WorkflowServer.class); Endpoint.publish(address, implementor); servicePublishRegistry.put(WorkflowServer.SERVICE_QNAME, Boolean.TRUE); } /** * 该段代码参考javaeye一篇帖子,作者为:步行者; * 链接是:http://www.iteye.com/topic/421068 * @param callbackService * @param _interface * @param serviceName callbackService.getName()+"_"+callbackService.getVersion() * @return */ protected List<Source> buildWSDL(CallbackService callbackService, InterfaceDef _interface,String address,String workDir,QName serviceQName,QName portQName) throws WSDLException,FileNotFoundException,IOException{ //为该Web service创建子目录 String fullDirName = workDir+File.separator+serviceQName.getLocalPart(); File serviceDir = new File(fullDirName); if (!serviceDir.exists()){ boolean b = serviceDir.mkdir(); if (!b){ throw new IOException("Can NOT create work dir for service "+serviceQName.toString()+"; the dir path is "+fullDirName); } } List<Source> result = new ArrayList<Source>(); List<OperationDef> operationDefs = _interface.getOperations(); if (operationDefs==null || operationDefs.size()==0){ //TODO 记录错误信息 return result; } OperationDef operationDef = operationDefs.get(0);//CallbackService仅允许一个Operation // 根据Interface定义构建WSDL,使用wsdl4j; String tns = callbackService.getTargetNamespaceUri(); WSDLFactory wsdlFactory = WSDLFactory.newInstance(); // 创建一个 WSDL definition Definition definition = wsdlFactory.newDefinition(); // 设置 targetNamespace definition.setTargetNamespace(tns); definition.setQName(new QName(tns, callbackService.getName())); // 添加命名空间 (一些常用的命名空间) definition.addNamespace("tns", tns); definition.addNamespace("xsd", NameSpaces.XSD.getUri()); definition.addNamespace("wsdlsoap", "http://schemas.xmlsoap.org/wsdl/soap/"); definition.addNamespace("soapenc11", "http://schemas.xmlsoap.org/soap/encoding/"); definition.addNamespace("soapenc12", "http://www.w3.org/2003/05/soap-encoding"); definition.addNamespace("soap11", "http://schemas.xmlsoap.org/soap/envelope/"); definition.addNamespace("soap12", "http://www.w3.org/2003/05/soap-envelope"); //0、创建data types XmlSchemaCollection schemaCollection = callbackService.getXmlSchemaCollection(); //将schemaCollections写入WsdlDefinitions.types段落? if (schemaCollection!=null){ Types types = definition.createTypes(); XmlSchema[] xmlSchemas = schemaCollection.getXmlSchemas(); for (XmlSchema xmlSchema : xmlSchemas){ if (!NameSpaces.XSD.getUri().equals(xmlSchema.getTargetNamespace())){ try { Document schemaDocument; schemaDocument = xmlSchema.getSchemaDocument(); SchemaImpl wsdlSchema = new SchemaImpl(); wsdlSchema.setRequired(true); wsdlSchema.setElementType(WSDLConstants.QNAME_SCHEMA); wsdlSchema.setElement(schemaDocument.getDocumentElement()); types.addExtensibilityElement(wsdlSchema); } catch (XmlSchemaSerializerException e) { throw new WSDLException(WSDLException.OTHER_ERROR,e.getMessage(),e); } } } definition.setTypes(types); } //一、创建消息 // 创建消息(Message) Message requestMsg = definition.createMessage(); requestMsg.setQName(new QName(tns, operationDef.getOperationName()+"RequestMessage")); requestMsg.setUndefined(false); Message responseMsg = definition.createMessage(); responseMsg.setQName(new QName(tns, operationDef.getOperationName()+"ResponseMessage")); responseMsg.setUndefined(false); definition.addMessage(requestMsg); definition.addMessage(responseMsg); // 创建RequestMessage的 Part Part part1 = definition.createPart(); part1.setName(operationDef.getOperationName()+"Request"); // 设置 part1 的 Schema Type QName part1Element = new QName(tns,operationDef.getOperationName()+"Request"); part1.setElementName(part1Element); requestMsg.addPart(part1); // 创建ResponseMessage的Part Part part2 = definition.createPart(); part2.setName(operationDef.getOperationName()+"Response"); // 设置 part2 的 Schema Type QName part2Element = new QName(tns,operationDef.getOperationName()+"Response"); part2.setElementName(part2Element); responseMsg.addPart(part2); // List<org.fireflow.model.data.Input> inputList = operationDef.getInputs(); // for (org.fireflow.model.data.Input ffInput:inputList){ // ffInput.getDataType(); // // // 创建 Part // Part part1 = definition.createPart(); // part1.setName(ffInput.getName()+"Part"); // // 设置 part1 的 Schema Type // part1.setTypeName(ffInput.getDataType()); // requestMsg.addPart(part1); // } // List<org.fireflow.model.data.Output> outputList = operationDef.getOutputs(); // for (org.fireflow.model.data.Output ffOutput : outputList){ // Part part2 = definition.createPart(); // part2.setName(ffOutput.getName()+"Part"); // // 设置 part2 的 Schema Type // part2.setTypeName(ffOutput.getDataType()); // // responseMsg.addPart(part2); // } //二、 创建 portType PortType portType = definition.createPortType(); portType.setQName(new QName(tns, _interface.getName()+"_PortType")); // 创建 Operation Operation operation = definition.createOperation(); operation.setName(operationDef.getOperationName()); // 创建 Input,并设置 Input 的 message Input input = definition.createInput(); input.setName(operationDef.getOperationName()+"Request"); input.setMessage(requestMsg); // 创建 Output,并设置 Output 的 message Output output = definition.createOutput(); output.setName(operationDef.getOperationName()+"Response"); output.setMessage(responseMsg); // 设置 Operation 的输入,输出,操作类型 operation.setInput(input); operation.setOutput(output); operation.setStyle(OperationType.REQUEST_RESPONSE); // 这行语句很重要 ! operation.setUndefined(false); portType.addOperation(operation); portType.setUndefined(false); definition.addPortType(portType); //三、创建绑定(binding) Binding binding = definition.createBinding(); binding.setQName(new QName(tns, _interface.getName()+"_Binding")); // 创建SOAP绑定(SOAP binding) SOAPBinding soapBinding = new SOAPBindingImpl(); // 设置 style = "document" soapBinding.setStyle("document"); // 设置 SOAP传输协议 为 HTTP soapBinding.setTransportURI("http://schemas.xmlsoap.org/soap/http"); // soapBinding 是 WSDL 中的扩展元素, // 为 binding 添加扩展元素 soapBinding binding.addExtensibilityElement(soapBinding); // 设置绑定的端口类型 binding.setPortType(portType); // 创建绑定操作(Binding Operation) BindingOperation bindingOperation = definition.createBindingOperation(); // 创建 bindingInput BindingInput bindingInput = definition.createBindingInput(); bindingInput.setName(operationDef.getOperationName()+"Request"); // 创建 SOAP body ,设置 use = "literal" SOAPBody soapBody1 = new SOAPBodyImpl(); soapBody1.setUse("literal"); bindingInput.addExtensibilityElement(soapBody1); BindingOutput bindingOutput = definition.createBindingOutput(); bindingOutput.setName(operationDef.getOperationName()+"Response"); SOAPBody soapBody2 = new SOAPBodyImpl(); soapBody2.setUse("literal"); bindingOutput.addExtensibilityElement(soapBody2); // 设置 bindingOperation 的名称,绑定输入 和 绑定输出 bindingOperation.setName(operationDef.getOperationName()); bindingOperation.setBindingInput(bindingInput); bindingOperation.setBindingOutput(bindingOutput); binding.addBindingOperation(bindingOperation); // 这行语句很重要 ! binding.setUndefined(false); definition.addBinding(binding); //四、创建Service及port // 创建 service Service service = definition.createService(); service.setQName(serviceQName); // 创建服务端口 port Port port = definition.createPort(); // 设置服务端口的 binding,名称,并添加SOAP地址 port.setBinding(binding); port.setName(portQName.getLocalPart()); SOAPAddress soapAddress = new SOAPAddressImpl(); soapAddress.setLocationURI(address); port.addExtensibilityElement(soapAddress); service.addPort(port); definition.addService(service); // 打印刚创建的 WSDL String wsdlFileName = fullDirName+File.separator+serviceQName.getLocalPart()+".wsdl"; File f = new File(wsdlFileName); if (f.exists()){ boolean b = f.delete(); if (!b){ throw new IOException("Can NOT delete old wsdl file '"+wsdlFileName+"'"); } } FileOutputStream fOut = new FileOutputStream(f); WSDLWriter writer = wsdlFactory.newWSDLWriter(); writer.writeWSDL(definition,fOut); fOut.flush(); StreamSource streamSource = new StreamSource(f); result.add(streamSource); return result; } }