View Javadoc

1   /*
2    * $Id: BexeeProvider.java,v 1.1 2004/12/15 14:18:15 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.axis;
10  
11  import java.util.HashMap;
12  import java.util.Iterator;
13  import java.util.Map;
14  import java.util.Vector;
15  
16  import javax.wsdl.Definition;
17  import javax.wsdl.WSDLException;
18  import javax.wsdl.factory.WSDLFactory;
19  import javax.wsdl.xml.WSDLWriter;
20  import javax.xml.namespace.QName;
21  import javax.xml.soap.SOAPException;
22  
23  import org.apache.axis.AxisFault;
24  import org.apache.axis.Message;
25  import org.apache.axis.MessageContext;
26  import org.apache.axis.description.OperationDesc;
27  import org.apache.axis.description.ServiceDesc;
28  import org.apache.axis.handlers.soap.SOAPService;
29  import org.apache.axis.message.RPCElement;
30  import org.apache.axis.message.RPCParam;
31  import org.apache.axis.message.SOAPEnvelope;
32  import org.apache.axis.providers.BasicProvider;
33  import org.w3c.dom.Document;
34  import org.w3c.dom.Element;
35  import org.xml.sax.SAXException;
36  
37  import bexee.core.BexeeMessage;
38  import bexee.core.Dispatcher;
39  import bexee.core.DispatcherException;
40  import bexee.dao.BPELProcessDAO;
41  import bexee.dao.DAOException;
42  import bexee.dao.DAOFactory;
43  import bexee.model.process.BPELProcess;
44  import bexee.wsdl.WSDLBindingFactory;
45  import bexee.wsdl.extensions.partnerlinktype.PartnerLinkTypeExtensionRegistry;
46  
47  /***
48   * This is the Axis provider (also commonly called pivot handler) used by all
49   * deployed BPEL process web services.
50   * <p>
51   * This provider takes the incoming SOAP message, performs a number of checks
52   * (e.g. verifying whether the requested operation can actually be performed by
53   * our <code>ProcessController</code>), puts all the necessary information
54   * into a new <code>BexeeMessage</code> and dispatches it to
55   * <code>Dispatcher</code>.
56   * <p>
57   * As soon as the provider receives the result back from the dispatcher, it will
58   * create a new SOAP response and hand it back to the Axis response flow, from
59   * where it will eventually be sent back to the client that invoked the service.
60   * 
61   * @version $Revision: 1.1 $, $Date: 2004/12/15 14:18:15 $
62   * @author Patric Fornasier
63   * @author Pawel Kowalski
64   */
65  public class BexeeProvider extends BasicProvider {
66  
67      /***
68       * The default URL for services
69       */
70      private static final String DEFAULT_SERVICES_URL = "http://localhost/bexee/services/";
71  
72      public void initServiceDesc(SOAPService service, MessageContext msgContext)
73              throws AxisFault {
74          // nothing to do here
75      }
76  
77      /***
78       * Creates a <code>BexeeMessage</code> from an incoming SOAP message and
79       * passes it to the {@link Dispatcher}from where it will be dispatched to
80       * the {@link bexee.core.ProcessController}.
81       * <p>
82       * Once the processing is completed, the result from the {@link Dispatcher}
83       * is taken and put back into a SOAP message and passed back into the Axis
84       * response flow.
85       * <p>
86       * Right now and Object of type {@link Element}is passed back into the
87       * response flow, because we don't generate Java objects from complex XML
88       * types.
89       * <p>
90       * TODO: This class needs some work, especially the message type stuff
91       * (possible improvements: code generation from wsdl, serializing and
92       * deserializing into Java objects)
93       */
94      public void invoke(MessageContext ctx) throws AxisFault {
95  
96          RPCElement body = null;
97          String operation = null;
98  
99          // initialize some variables
100         String service = ctx.getTargetService();
101         SOAPEnvelope reqEnv = ctx.getRequestMessage().getSOAPEnvelope();
102         ServiceDesc serviceDesc = ctx.getService().getServiceDescription();
103         OperationDesc operationDesc = ctx.getOperation();
104 
105         // find the first 'root' body element, which is the RPC call
106         Vector bodies = reqEnv.getBodyElements();
107         for (int i = 0; body == null && i < bodies.size(); i++) {
108             if (bodies.get(i) instanceof RPCElement) {
109                 body = (RPCElement) bodies.get(i);
110             }
111         }
112 
113         // if we didn't find anything, throw an exception
114         if (body == null) {
115             throw new AxisFault("No RPCElement found in SOAP message");
116         }
117 
118         // check if that service provides the requested operation
119         if (operationDesc == null) {
120             QName qname = new QName(body.getNamespaceURI(), body.getName());
121             operationDesc = serviceDesc.getOperationByElementQName(qname);
122         }
123 
124         // if not, throw an exception
125         if (operationDesc == null) {
126             throw new AxisFault("No such operation");
127         }
128 
129         // get RPC parameters (parts) (checks also for too many params)
130         Vector params = null;
131         try {
132             params = body.getParams();
133         } catch (SAXException e) {
134             throw new AxisFault("Possible cause: too many parameters", e);
135         }
136 
137         // check if enough params provided
138         if (operationDesc.getNumParams() > params.size()) {
139             throw new AxisFault("Wrong number of parameters passed in");
140         }
141 
142         // TODO check if parameter types match
143 
144         // looks ok, get name of invoked operation
145         operation = body.getMethodName();
146 
147         // create message for bexee engine and dispatch
148         BexeeMessage message = createBexeeMessage(service, operation, params);
149         Object result;
150         try {
151             result = dispatch(message);
152         } catch (DispatcherException e) {
153             throw new AxisFault("Unable to dispatch message", e);
154         }
155         // create RPC response element
156         RPCElement resBody = new RPCElement(operation + "Response");
157         resBody.setPrefix(body.getPrefix());
158         resBody.setNamespaceURI(body.getNamespaceURI());
159         try {
160             resBody.setEncodingStyle(ctx.getEncodingStyle());
161         } catch (SOAPException e) {
162             throw AxisFault.makeFault(e);
163         }
164 
165         // create RPC parameter containing result and add it to result body
166         RPCParam param = new RPCParam(operationDesc.getReturnQName(), result);
167         resBody.addParam(param);
168 
169         // set up a SOAP envelope with appropriate SOAP and schema versions
170         SOAPEnvelope resEnv = new SOAPEnvelope();
171         resEnv.setSoapConstants(ctx.getSOAPConstants());
172         resEnv.setSchemaVersion(ctx.getSchemaVersion());
173 
174         // add the result body to the SOAP response envelope
175         resEnv.addBodyElement(resBody);
176 
177         // create the response message and put it into the message context
178         ctx.setResponseMessage(new Message(resEnv));
179     }
180 
181     /***
182      * Gets WSDL associated with requested BPEL process from DAO and adds
183      * binding information on the fly.
184      * <p>
185      * If the target service endpoint URL can not be determined, a constant
186      * value with the default services root is assumed.
187      */
188     public void generateWSDL(MessageContext ctx) throws AxisFault {
189 
190         // the WSDL Document
191         Document doc = null;
192 
193         // get the service location url used for binding
194         String url = getLocationURL(ctx);
195 
196         // get WSDL from BPEL process
197         BPELProcessDAO dao = DAOFactory.getInstance().createBPELProcessDAO();
198         BPELProcess process;
199         String name = getServiceName(ctx);
200         try {
201             process = dao.find(name);
202         } catch (DAOException e) {
203             throw new AxisFault("Error trying to find process", e);
204         }
205 
206         if (process == null) {
207             throw new AxisFault("No process " + name + " found");
208         }
209 
210         Definition wsdlAbstract = process.getWSDL();
211 
212         try {
213             // add binding information to wsdl
214             WSDLBindingFactory bindingFactory = new WSDLBindingFactory();
215             Definition wsdl = bindingFactory.addBinding(wsdlAbstract, url);
216 
217             // convert into org.w3c.dom.Document
218             WSDLWriter writer = WSDLFactory.newInstance().newWSDLWriter();
219             wsdl.setExtensionRegistry(new PartnerLinkTypeExtensionRegistry());
220             doc = writer.getDocument(wsdl);
221         } catch (WSDLException e) {
222             throw AxisFault.makeFault(e);
223         }
224 
225         // save WSDL Document in message context
226         ctx.setProperty("WSDL", doc);
227     }
228 
229     /***
230      * Gets the service location URL.
231      * 
232      * @return a <code>String</code>
233      */
234     protected String getLocationURL(MessageContext ctx) {
235         // location URL is whatever is explicitly set in the MC
236         String url = ctx.getStrProp(MessageContext.WSDLGEN_SERV_LOC_URL);
237 
238         // If nothing, try what's explicitly set in the ServiceDesc
239         if (url == null) {
240             url = ctx.getService().getServiceDescription().getEndpointURL();
241         }
242 
243         // If nothing, use the actual transport URL
244         if (url == null) {
245             url = ctx.getStrProp(MessageContext.TRANS_URL);
246         }
247 
248         // if everything fails, hardcode it
249         if (url == null) {
250             url = DEFAULT_SERVICES_URL + getServiceName(ctx);
251         }
252 
253         return url;
254     }
255 
256     protected String getServiceName(MessageContext ctx) {
257         String name = ctx.getService().getServiceDescription().getName();
258         return name;
259     }
260 
261     /***
262      * Helper method to create the <code>BexeeMessage</code>
263      */
264     protected BexeeMessage createBexeeMessage(String service, String operation,
265             Vector params) throws AxisFault {
266 
267         BexeeMessage message = new BexeeMessage();
268 
269         // create parts map
270         Map parts = new HashMap();
271         for (Iterator iter = params.iterator(); iter.hasNext();) {
272             RPCParam rpcParam = (RPCParam) iter.next();
273 
274             // get the value out of the RPC param
275             Object value = rpcParam.getObjectValue();
276 
277             // get the name we defined in the service description (we do not
278             // care how the parameter was really called in the RPC call)
279             String name = rpcParam.getParamDesc().getName();
280 
281             // store object
282             parts.put(name, value);
283         }
284 
285         // set fields on BexeeMessage
286         message.setService(service);
287         message.setOperation(operation);
288         message.setParts(parts);
289 
290         return message;
291     }
292 
293     /***
294      * Call dispatcher to kick off the process controller and return result
295      * 
296      * @return the result from the {@link Dispatcher}
297      */
298     protected Object dispatch(BexeeMessage message) throws DispatcherException {
299 
300         Dispatcher dispatcher = new Dispatcher(message);
301         return dispatcher.dispatch();
302     }
303 
304 }