View Javadoc

1   /* $Id: HttpApplication.java,v 1.8 2004/10/21 10:05:12 pah Exp $
2    * Created on Jul 24, 2004
3    *
4    * Copyright (C) AstroGrid. All rights reserved.
5    *
6    * This software is published under the terms of the AstroGrid 
7    * Software License version 1.2, a copy of which has been included 
8    * with this distribution in the LICENSE.txt file.  
9    *
10   */
11  package org.astrogrid.applications.http;
12  
13  import java.util.Enumeration;
14  import java.util.HashMap;
15  import java.util.Iterator;
16  import java.util.Map;
17  
18  import org.apache.commons.logging.Log;
19  import org.apache.commons.logging.LogFactory;
20  import org.astrogrid.applications.AbstractApplication;
21  import org.astrogrid.applications.CeaException;
22  import org.astrogrid.applications.Status;
23  import org.astrogrid.applications.beans.v1.Script;
24  import org.astrogrid.applications.beans.v1.SimpleParameter;
25  import org.astrogrid.applications.beans.v1.WebHttpApplicationSetup;
26  import org.astrogrid.applications.beans.v1.WebHttpCall;
27  import org.astrogrid.applications.beans.v1.types.HttpMethodType;
28  import org.astrogrid.applications.description.ApplicationInterface;
29  import org.astrogrid.applications.http.HttpServiceClient.HttpServiceType;
30  import org.astrogrid.applications.http.exceptions.HttpApplicationNetworkException;
31  import org.astrogrid.applications.http.exceptions.HttpApplicationWebServiceURLException;
32  import org.astrogrid.applications.http.script.IdentityPreprocessor;
33  import org.astrogrid.applications.http.script.Preprocessor;
34  import org.astrogrid.applications.http.script.XSLTPreprocessor;
35  import org.astrogrid.applications.parameter.ParameterAdapter;
36  import org.astrogrid.applications.parameter.protocol.ProtocolLibrary;
37  import org.astrogrid.registry.beans.cea.CeaHttpApplicationType;
38  import org.astrogrid.workflow.beans.v1.Tool;
39  
40  /***
41   * An Application that calls an http service, such as a SIAP service. ?
42   * Responsible for extracting the relevant info from the application description
43   * and passing them to a HttpServiceClient that knows how to call the web
44   * service. Handles the threading (so execute doesn't block) and status settings
45   * during execution. ?
46   * 
47   * 
48   * 
49   * @author jdt
50   */
51  public class HttpApplication extends AbstractApplication  {
52      /***
53       * Commons Logger for this class
54       */
55      private static final Log log = LogFactory.getLog(HttpApplication.class);
56  
57      /***
58       * Ctor
59       * 
60       * @param id
61       * @param tool
62       * @param applicationInterface
63       * @param protocolLibrary
64       */
65      public HttpApplication(IDs id, Tool tool, ApplicationInterface applicationInterface, ProtocolLibrary protocolLibrary) {
66          super(id, tool, applicationInterface, protocolLibrary);
67      }
68  
69      /***
70       * Combines a the Tool and Application docs and transforms to a doc suitable
71       * for calling the web service.  For this we use the pre-processing element in the CeaHttpApplictionType
72       * doc, or a default if none is available.
73       */
74      private WebHttpCall createCallingDocument(final Tool tool, final CeaHttpApplicationType app)  {
75          if (log.isTraceEnabled()) {
76              log.trace("createCallingDocument(Tool tool = " + tool + ", CeaHttpApplicationType app = " + app
77                      + ") - start");
78          }
79  
80              final WebHttpApplicationSetup httpAppInfo = app.getCeaHttpAdapterSetup();
81              final Script preprocessScript = httpAppInfo.getPreProcessScript();
82              
83              log.debug("preprocessScript="+preprocessScript);
84              if (preprocessScript!=null) {
85                  log.debug("preprocessScript lang="+preprocessScript.getLang());
86                  log.debug("preprocessScript code="+preprocessScript.getCode());
87              }
88              
89              Preprocessor preprocessor;
90              if (preprocessScript==null) {
91                  log.debug("Preprocessing with identity preprocessor");
92                  preprocessor = new IdentityPreprocessor();
93              } else {
94                  String code = preprocessScript.getCode();
95                  assert code!=null;
96                  if (preprocessScript.getLang().equals(XSLTPreprocessor.xmlName)) {
97                      log.debug("Preprocessing with XSLT preprocessor");
98                      preprocessor = new XSLTPreprocessor(code);
99                      throw new UnsupportedOperationException("preprocessing scripts have not been implemented yet");
100                 } else {
101                     log.debug("Non-xslt preprocessor - cannot process");
102                     throw new UnsupportedOperationException("Non-xslt scripts have not been implemented yet");
103                 }
104             }
105             
106         WebHttpCall returnWebHttpCall = preprocessor.process(tool, app);
107         if (log.isTraceEnabled()) {
108             log.trace("createCallingDocument(Tool, CeaHttpApplicationType) - end - return value = "
109                             + returnWebHttpCall);
110         }
111             return returnWebHttpCall;
112     }
113 
114     public Runnable createExecutionTask() throws CeaException {
115         createAdapters();
116         log.debug("createExecutionTask() - creating worker thread");
117         Runnable task = new Exec();
118         setStatus(Status.INITIALIZED);
119         return task;
120     }
121 
122 public boolean execute() throws CeaException {
123     if (log.isTraceEnabled()) {
124         log.trace("execute() - start");
125     }
126     log.debug("execute() - starting thread");
127     (new Thread(createExecutionTask())).start();
128     if (log.isTraceEnabled()) {
129             log.trace("execute() - end - return value = " + true);
130     }        
131     return true;
132     }    
133 
134     private class Exec implements Runnable {
135     
136        /***
137      * Where the action happens
138      */
139     public void run() {
140         if (log.isTraceEnabled()) {
141             log.trace("run() - start");
142         }
143         //
144         //  The arguments must be processed before the tool document gets
145         //  turned into a calling document.
146         log.debug("Processing arguments");        
147         try{
148             for (Iterator i = inputParameterAdapters(); i.hasNext();) {
149                 ParameterAdapter a = (ParameterAdapter) i.next();
150                 final String name = a.getWrappedParameter().getName();
151                 final Object value = a.process();
152                 //Replace Parameters in the tool document
153                 //with their processed values
154                 //This might be a bad idea...
155                 //Why not just extract the values and send them to the HttpServiceClient?  I want to have an actual Tool xml
156                 //document with the correct parameter values that I can transform using the registry/user supplied xslt.
157                 a.getWrappedParameter().setValue((String) value);
158                 log.debug(name + "=" + value);
159             }
160             //Prepare calling document, and extract what we need for the http call
161             final HttpApplicationDescription description = (HttpApplicationDescription) getApplicationDescription();
162             final CeaHttpApplicationType app = description.getApplication();
163             final WebHttpCall httpCall = createCallingDocument(getTool(),app);
164             
165             final String url = httpCall.getURL().getContent();
166             final HttpMethodType requestedMethod = httpCall.getURL().getMethod();        
167             HttpServiceClient.HttpServiceType method;
168             if (HttpMethodType.GET.equals(requestedMethod)) {
169                 method = HttpServiceClient.HttpServiceType.GET;
170                 log.debug("run() - Using http-get");
171             } else if (HttpMethodType.POST.equals(requestedMethod)) {
172                 method = HttpServiceClient.HttpServiceType.POST;
173                 log.debug("run() - using http-post");
174             } else {  //@TODO refactor exceptions
175                 reportError("Unknown http method requested"); //this really shouldn't happen, given the constraints in the schema
176                 return;
177             }            
178             final Enumeration enum = httpCall.enumerateSimpleParameter();
179             Map inputArguments = new HashMap();
180             while (enum.hasMoreElements()) {
181                 final SimpleParameter parameter = (SimpleParameter) enum.nextElement();
182                 assert parameter!=null;
183                 assert parameter.getName()!=null;
184                 assert parameter.getValue()!=null;
185                 inputArguments.put(parameter.getName(), parameter.getValue());
186             }
187                     
188             setStatus(Status.RUNNING);
189             log.info("calling url="+url);
190             HttpServiceClient client = new HttpServiceClient(url, method);
191             final String resultText = client.call(inputArguments);
192             log.debug("run() - unprocessed result:  : resultText = " + resultText);
193             
194             final String processedResult = postProcess(resultText);
195             
196             // we can do this, as we know there's only ever going to be one output parameter.
197             setStatus(Status.WRITINGBACK);
198             Iterator outputParamsIt = outputParameterAdapters();
199             ParameterAdapter result = (ParameterAdapter) outputParamsIt.next();
200             assert !outputParamsIt.hasNext() : "Expect there to be only one output parameter for an HttpApplication";
201             result.writeBack(processedResult);
202             log.info("completed call successfully");
203             setStatus(Status.COMPLETED);
204         } catch (CeaException e) {
205             log.error("run() - failed to write back param values", e);
206             reportError("Failed to write back parameter values", e);
207         } catch (HttpApplicationWebServiceURLException e) {
208             log.error("run() - problem with service URL", e);
209             reportError("Error 404 - Not Found from the application's URL", e);
210         } catch (HttpApplicationNetworkException e) {
211             log.error("run()", e);
212             reportError("Network Error while contacting web server",e);
213         } catch (Throwable t) {
214             log.error("run()", t);
215 
216             reportError("Something else gone wrong", t);
217         }
218 
219         if (log.isTraceEnabled()) {
220             log.trace("run() - end");
221         }
222     }
223     }
224 
225     /***
226      * @param result
227      * @return
228      * @TODO this method is incomplete
229      */
230     private String postProcess(final String result) {
231         if (log.isTraceEnabled()) {
232             log.trace("postProcess(String result = " + result + ") - start");
233         }
234 
235         log.debug("No post-processing at present");
236 
237         if (log.isTraceEnabled()) {
238             log.trace("postProcess(String) - end - return value = " + result);
239         }
240         return result;
241     }
242 
243 }
244 
245 /*
246  * $Log: HttpApplication.java,v $
247  * Revision 1.8  2004/10/21 10:05:12  pah
248  * log the target url
249  *
250  * Revision 1.7  2004/09/26 23:29:38  jdt
251  * Put the processing of the parameters back before the creation of the calling document.  The calling doc is
252  * created using whatever values are in the Tool doc at the time, so the parameter processing must be done first.
253  * Perhaps needs some rejigging if it's misleading.
254  *
255  * Revision 1.6  2004/09/17 01:23:01  nw
256  * altered to make use of threadpooling
257  *
258  * Revision 1.5.18.1  2004/09/14 15:16:47  nw
259  * adjusted to new threading model.
260  *
261  * Revision 1.5  2004/09/01 15:42:26  jdt
262  * Merged in Case 3
263  *
264  * Revision 1.1.4.15  2004/08/30 14:54:42  jdt
265  * tidied some imports
266  *
267  * Revision 1.1.4.14  2004/08/18 11:41:32  jdt
268  * added some logging
269  *
270  * Revision 1.1.4.13  2004/08/15 10:57:42  jdt
271  * removed extraneous stuff
272  *
273  * Revision 1.1.4.12  2004/08/12 13:17:11  jdt
274  * factored some stuff back into its own thread
275  *
276  * Revision 1.1.4.11  2004/08/12 12:15:09  jdt
277  * you foolish boy.
278  *
279  * Revision 1.1.4.10  2004/08/11 22:55:35  jdt
280  * Refactoring, and a lot of new unit tests.
281  *
282  * Revision 1.1.4.9  2004/08/10 13:44:23  jdt
283  * Following new workflow-object types...and a safety checkin.
284  *
285  * Revision 1.1.4.8  2004/08/09 16:37:13  jdt
286  * Brought into line following pah's suggested schema changes
287  *
288  * Revision 1.1.4.7  2004/08/06 13:30:22  jdt
289  * safety checkin
290  * Revision 1.1.4.6 2004/07/30 16:59:50 jdt
291  * limping along.
292  * 
293  * Revision 1.1.4.5 2004/07/30 13:11:28 jdt wired up the parameters
294  * 
295  * Revision 1.1.4.4 2004/07/29 21:30:47 jdt *** empty log message ***
296  * 
297  * Revision 1.1.4.3 2004/07/29 17:08:22 jdt Think about how I'm going to get
298  * stuff out of the registry
299  * 
300  * Revision 1.1.4.2 2004/07/27 19:28:43 jdt fix build errors
301  * 
302  * Revision 1.1.4.1 2004/07/27 17:20:25 jdt merged from applications_JDT_case3
303  * 
304  * Revision 1.1.2.2 2004/07/24 18:43:29 jdt Started plumbing in the httpclient
305  * Revision 1.1.2.1 2004/07/24 17:16:16 jdt Borrowed from javaclass
306  * application....stealing is always quicker.
307  *  
308  */