View Javadoc

1   /*
2    * $Id: Manager.java,v 1.1 2004/12/15 14:18:08 patforna Exp $
3    *
4    * Copyright (c) 2004 Patric Fornasier, Pawel Kowalski
5    * Berne University of Applied Sciences
6    * School of Engineering and Information Technology
7    * All rights reserved.
8    */
9   package bexee.admin;
10  
11  import java.io.ByteArrayInputStream;
12  import java.io.InputStream;
13  import java.util.ArrayList;
14  import java.util.Collection;
15  import java.util.Hashtable;
16  import java.util.Iterator;
17  import java.util.List;
18  import java.util.Map;
19  
20  import javax.wsdl.Definition;
21  import javax.wsdl.Input;
22  import javax.wsdl.Operation;
23  import javax.wsdl.Output;
24  import javax.wsdl.Part;
25  import javax.wsdl.PortType;
26  import javax.wsdl.extensions.ExtensionRegistry;
27  import javax.wsdl.factory.WSDLFactory;
28  import javax.wsdl.xml.WSDLReader;
29  import javax.xml.namespace.QName;
30  
31  import org.apache.axis.AxisEngine;
32  import org.apache.axis.AxisFault;
33  import org.apache.axis.ConfigurationException;
34  import org.apache.axis.EngineConfiguration;
35  import org.apache.axis.MessageContext;
36  import org.apache.axis.configuration.FileProvider;
37  import org.apache.axis.deployment.wsdd.WSDDConstants;
38  import org.apache.axis.deployment.wsdd.WSDDDeployment;
39  import org.apache.axis.deployment.wsdd.WSDDException;
40  import org.apache.axis.deployment.wsdd.WSDDOperation;
41  import org.apache.axis.deployment.wsdd.WSDDService;
42  import org.apache.axis.deployment.wsdd.WSDDUndeployment;
43  import org.apache.axis.description.OperationDesc;
44  import org.apache.axis.description.ParameterDesc;
45  import org.apache.axis.description.ServiceDesc;
46  import org.apache.axis.handlers.soap.SOAPService;
47  import org.apache.axis.utils.XMLUtils;
48  import org.apache.commons.logging.Log;
49  import org.apache.commons.logging.LogFactory;
50  import org.w3c.dom.Document;
51  
52  import bexee.dao.BPELProcessDAO;
53  import bexee.dao.DAOException;
54  import bexee.dao.DAOFactory;
55  import bexee.model.BPELDocumentException;
56  import bexee.model.BPELProcessFactory;
57  import bexee.model.process.BPELProcess;
58  import bexee.wsdl.extensions.partnerlinktype.PartnerLinkTypeExtensionRegistry;
59  
60  /***
61   * The <code>Manager</code> is used in bexee for deployment of BPEL business
62   * processes and the related WSLD descriptions.
63   * 
64   * @version $Revision: 1.1 $, $Date: 2004/12/15 14:18:08 $
65   * @author Patric Fornasier
66   * @author Pawel Kowalski
67   */
68  public class Manager {
69  
70      private static Log log = LogFactory.getLog(Manager.class);
71  
72      /***
73       * Deploys a BPEL process to the bexee engine and registers it in Axis as a
74       * web service. This method performs the following operations in the given
75       * order:
76       * <p>
77       * <ol>
78       * <li>Checks the WSDL and BPEL namespaces</li>
79       * <li>Create a new <code>BPELProcess</code> with a
80       * <code>BPELProcessFactory</code></li>
81       * <li>Deploys a new web service to Axis under the name defined in the BPEL
82       * document</li>
83       * <li>Returns a <code>SOAPBodyElement</code> of the form
84       * &lt;manager&gt;result text&lt;/manager&gt;
85       * </ol>
86       * 
87       * @param input
88       *            <code>elems[0]</code> must be an <code>Element</code>
89       *            representing a BPEL document , while <code>elems[1]</code>
90       *            must be an <code>Element</code> represent a BPEL document.
91       *            elems[1..n] can be further additional WSDL documents.
92       * 
93       * @return an <code>Element[]</code>, whose first element contains a
94       *         message that the service was deployed.
95       */
96      public String deploy(String[] input) throws DeploymentException {
97  
98          if (input == null || input.length < 2) {
99              throw new DeploymentException("at least two elems expected");
100         }
101 
102         // declare some variables
103         BPELProcess bpel = null;
104         Definition wsdl = null;
105         List partnerWSDL = new ArrayList();
106 
107         // get reference to Axis engine and its deployment data
108         AxisEngine axis = getAxisEngine();
109         WSDDDeployment deployment = getDeployment();
110 
111         try {
112             // try to create a new BPELProcess from the input we've got
113             bpel = createBPELProcess(input[0]);
114         } catch (BPELDocumentException e) {
115             throw new DeploymentException("Unable to create BPELProcess", e);
116         }
117 
118         try {
119             // create WSDL Definition object using WSDL4J
120             WSDLReader wsdlReader = WSDLFactory.newInstance().newWSDLReader();
121 
122             // register extension partner links
123             ExtensionRegistry reg = new PartnerLinkTypeExtensionRegistry();
124             wsdlReader.setExtensionRegistry(reg);
125             wsdlReader.setFeature("javax.wsdl.importDocuments", true);
126 
127             // create Definition for process WSDL
128             InputStream is = new ByteArrayInputStream(input[1].getBytes());
129             Document doc = XMLUtils.newDocument(is);
130             wsdl = wsdlReader.readWSDL(null, doc);
131 
132             // create Definition for additional WSDL
133             for (int i = 2; i < input.length; i++) {
134                 is = new ByteArrayInputStream(input[i].getBytes());
135                 doc = XMLUtils.newDocument(is);
136                 Definition definition = wsdlReader.readWSDL(null, doc);
137                 partnerWSDL.add(definition);
138             }
139         } catch (Exception e) {
140             throw new DeploymentException("Unable to create Definition", e);
141         }
142 
143         // create a new Axis service for each port type defined in the WSDL
144         Collection portTypes = wsdl.getPortTypes().values();
145         String targetNS = wsdl.getTargetNamespace();
146         for (Iterator portIter = portTypes.iterator(); portIter.hasNext();) {
147             PortType portType = (PortType) portIter.next();
148 
149             // create the new WSDD service
150             WSDDService service = new WSDDService();
151             ServiceDesc serviceDesc = service.getServiceDesc();
152 
153             // set names
154             // service.setQName(portType.getQName());
155             // service.setName(portType.getQName().getLocalPart());
156             // serviceDesc.setName(portType.getQName().getLocalPart());
157 
158             // FIXME right now only one port type is supported!!!
159             String name = bpel.getName();
160             service.setQName(new QName(name));
161             service.setName(name);
162             serviceDesc.setName(name);
163 
164             // our own provider should handle this service -> java:BexeeProvider
165             service.setProviderQName(new QName(WSDDConstants.URI_WSDD_JAVA,
166                     "BexeeProvider"));
167 
168             // set namespace mappings
169             Map nsMap = wsdl.getNamespaces();
170             serviceDesc.setNamespaceMappings(new ArrayList(nsMap.entrySet()));
171 
172             // add operations to service description
173             List operations = portType.getOperations();
174             for (Iterator oIter = operations.iterator(); oIter.hasNext();) {
175                 Operation operation = (Operation) oIter.next();
176 
177                 OperationDesc operationDesc = new OperationDesc();
178                 operationDesc.setName(operation.getName());
179 
180                 // add input parameters (parts) to operation description
181                 Input in = operation.getInput();
182                 Collection inParts = in.getMessage().getParts().values();
183                 for (Iterator partIter = inParts.iterator(); partIter.hasNext();) {
184                     Part part = (Part) partIter.next();
185                     ParameterDesc paramDesc = new ParameterDesc();
186                     paramDesc.setName(part.getName());
187 
188                     paramDesc.setQName(new QName(targetNS, part.getName()));
189 
190                     paramDesc.setTypeQName(part.getTypeName());
191                     paramDesc.setMode(ParameterDesc.IN);
192 
193                     operationDesc.addParameter(paramDesc);
194                 }
195 
196                 // make sure that there's only one part in the output message
197                 Output out = operation.getOutput();
198                 Collection outParts = out.getMessage().getParts().values();
199                 if (outParts.size() == 0) {
200                     throw new DeploymentException(
201                             "No message part defined for output in operation "
202                                     + operation.getName());
203                 }
204                 if (outParts.size() > 1) {
205                     throw new DeploymentException(
206                             "More than one message part defined for output in operation "
207                                     + operation.getName());
208                 }
209 
210                 /*
211                  * set operation return information note that it is not possible
212                  * to use our own namespace prefixes. The only way to do this is
213                  * upon rendering the wsdd by calling the SerializationContext
214                  * registerPrefixForURI() method
215                  */
216                 Part part = (Part) outParts.iterator().next();
217                 operationDesc
218                         .setReturnQName(new QName(targetNS, part.getName()));
219                 operationDesc.setReturnType(part.getTypeName());
220 
221                 service.addOperation(new WSDDOperation(operationDesc));
222             }
223 
224             /*
225              * The parameters variable in the Axis class
226              * org.apache.axis.deployment.wsdd.DeployableItem doesn't get
227              * initialized when using the no-arg constructor. This results in a
228              * NullPointerException when calling service.validateDescriptors().
229              * Thus we have to somehow initialize this variable which can be
230              * achieved by calling the method on the next line. Hopefully this
231              * will be fixed in a future release of Axis.
232              */
233             service.setOptionsHashtable(new Hashtable());
234 
235             try {
236                 // validate descriptor before deploying service
237                 service.validateDescriptors();
238             } catch (WSDDException e) {
239                 throw new DeploymentException("Invalid WSDD", e);
240             }
241 
242             // add replace the service to the deployment configuration
243             // existing services with the same name will be replaced
244             deployment.deployService(service);
245         }
246 
247         try {
248             // notify the engine of the changes (this will also save the wsdd)
249             axis.refreshGlobalOptions();
250         } catch (ConfigurationException e) {
251             throw new DeploymentException("Unable to refresh Axis config", e);
252         }
253 
254         // add WSDL file describing BPEL process to BPELprocess
255         bpel.setWSDL(wsdl);
256 
257         // add partner WSDL files
258         bpel.addPartnerWSDL(partnerWSDL);
259 
260         // stroe BPEL process
261         DAOFactory factory = DAOFactory.getInstance();
262         BPELProcessDAO dao = factory.createBPELProcessDAO();
263         String id;
264         try {
265             id = dao.replace(bpel);
266         } catch (DAOException e) {
267             throw new DeploymentException("Unable to insert/update process", e);
268         }
269 
270         return "BPEL process succesfully updated, id: " + id;
271     }
272 
273     public String undeploy(String name) throws DeploymentException {
274 
275         String result = null;
276 
277         AxisEngine axis = getAxisEngine();
278         WSDDDeployment deployment = getDeployment();
279 
280         SOAPService service;
281         try {
282             service = axis.getService("TravelProcess");
283         } catch (AxisFault e) {
284             throw new DeploymentException("Unable to get service");
285         }
286 
287         // we don't want to throw an exception here
288         if (service == null) {
289             return "No such service found to undeploy, name: " + name;
290         }
291 
292         // create undeployment descriptor
293         WSDDUndeployment undeployment = new WSDDUndeployment();
294         undeployment.addService(new QName("", name));
295 
296         try {
297             // undeploy from Axis
298             undeployment.undeployFromRegistry(deployment);
299             axis.refreshGlobalOptions();
300         } catch (ConfigurationException e) {
301             throw new DeploymentException("Unable to undeploy service '" + name
302                     + "' from Axis");
303         }
304         try {
305             // TODO: Do we really want to delete the process and context data?
306             // undeploy from bexee
307             DAOFactory factory = DAOFactory.getInstance();
308             BPELProcessDAO dao = factory.createBPELProcessDAO();
309             dao.delete(name);
310             // TODO: delete ProcessContext instances
311         } catch (DAOException e) {
312             throw new DeploymentException("Unable to undeploy from bexee", e);
313         }
314 
315         return "Service succesfully undeployed, name: " + name;
316     }
317 
318     /***
319      * Get the WSDD Deployment from the Axis engine
320      */
321     private WSDDDeployment getDeployment() throws DeploymentException {
322 
323         WSDDDeployment deployment = null;
324 
325         AxisEngine axis = getAxisEngine();
326 
327         // get the engine configuration
328         EngineConfiguration config = axis.getConfig();
329 
330         /*
331          * We are using a wsdd file to store configuration data. Unfortunately
332          * Axis doesn't provide all the functionality we need in the
333          * EngineConfiguration interface, so we have to make a downcast here :(
334          */
335         if (config instanceof FileProvider) {
336             deployment = ((FileProvider) config).getDeployment();
337         } else {
338             throw new DeploymentException("WSDDDeployment supported only!");
339         }
340         return deployment;
341     }
342 
343     /***
344      * Get the Axis Engine
345      */
346     private AxisEngine getAxisEngine() {
347         // get message context to gain access to Axis engine
348         MessageContext ctx = MessageContext.getCurrentContext();
349         AxisEngine axis = ctx.getAxisEngine();
350         return axis;
351     }
352 
353     /***
354      * Create a new <code>BPELProcess</code> given a bpel xml document.
355      * 
356      * @param bpel
357      *            the bpel document tree
358      * @return a <code>BPELProcess</code>
359      * @throws BPELDocumentException
360      */
361     protected BPELProcess createBPELProcess(String bpel)
362             throws BPELDocumentException {
363         // convert bpel process to stream and let factory create a BPELProcess
364         byte[] bytes = bpel.getBytes();
365         InputStream is = new ByteArrayInputStream(bytes);
366         return BPELProcessFactory.getInstance().createBPELProcess(is);
367     }
368 }