View Javadoc

1   /*$Id: DatacenterApplication.java,v 1.4 2006/06/15 16:50:08 clq2 Exp $
2    * Created on 12-Jul-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.dataservice.service.cea;
12  
13  import EDU.oswego.cs.dl.util.concurrent.Executor;
14  import java.io.IOException;
15  import java.io.StringWriter;
16  import java.net.URISyntaxException;
17  import java.net.URL;
18  import java.security.Principal;
19  import org.apache.commons.logging.Log;
20  import org.apache.commons.logging.LogFactory;
21  import org.astrogrid.applications.AbstractApplication;
22  import org.astrogrid.applications.CeaException;
23  import org.astrogrid.applications.Status;
24  import org.astrogrid.applications.beans.v1.parameters.ParameterValue;
25  import org.astrogrid.applications.description.ApplicationInterface;
26  import org.astrogrid.applications.parameter.protocol.ProtocolLibrary;
27  import org.astrogrid.dataservice.queriers.Querier;
28  import org.astrogrid.dataservice.queriers.QuerierListener;
29  import org.astrogrid.dataservice.queriers.status.QuerierStatus;
30  import org.astrogrid.dataservice.service.AxisDataServer;
31  import org.astrogrid.dataservice.service.DataServer;
32  import org.astrogrid.io.account.LoginAccount;
33  import org.astrogrid.io.mime.MimeNames;
34  import org.astrogrid.query.Query;
35  import org.astrogrid.query.returns.ReturnTable;
36  import org.astrogrid.query.QueryException;
37  import org.astrogrid.query.QueryState;
38  import org.astrogrid.slinger.sourcetargets.HomespaceSourceTarget;
39  import org.astrogrid.slinger.sourcetargets.URISourceTargetMaker;
40  import org.astrogrid.slinger.targets.TargetIdentifier;
41  import org.astrogrid.slinger.targets.WriterTarget;
42  import org.astrogrid.workflow.beans.v1.Tool;
43  import org.xml.sax.SAXException;
44  import org.astrogrid.slinger.homespace.HomespaceName;
45  import org.astrogrid.slinger.ivo.IVORN;
46  
47  /*** Represents a query running against the datacenter.
48   * <p />
49   * Datacenter already has a framework for starting asynchronous queries and then monitoring their progress.
50   * This class wraps the datacenter framework, mapping querier events into events in the CEA framework.
51   *
52   * There is one instance of this class for each query
53   *
54   * @author Noel Winstanley nw@jb.man.ac.uk 12-Jul-2004
55   * @author K Andrews
56   *
57   */
58  public class DatacenterApplication extends AbstractApplication implements QuerierListener {
59     
60     /***
61      * Commons Logger for this class
62      */
63     private static final Log logger = LogFactory.getLog(DatacenterApplication.class);
64     
65     protected final Principal acc;
66     /*** the datacenter system object - a static, which provides access to the datacenter query framework */
67     protected final DataServer serv;
68     /*** the executor for background tasks */
69     Executor exec;
70     
71     /*** id allocated by datacenter to this querier */
72     protected String querierID;
73     
74     public String getQuerierId() { return querierID;   }
75     
76     /*** Construct a new DatacetnerApplication
77      * @param arg0
78      * @param arg1
79      * @param arg2
80      * @param arg3
81      */
82     public DatacenterApplication(IDs ids, Tool tool, ApplicationInterface interf, ProtocolLibrary arg3,DataServer serv,Executor exec) {
83        super(ids, tool,interf, arg3);
84        this.serv = serv;
85        this.exec = exec;
86        this.acc = new LoginAccount(ids.getUser().getUserId(),ids.getUser().getCommunity());
87        logger.info("CEA DSA initialised, Job ID="+ids.getJobStepId());
88     }
89     
90     /*** construct a query from the contents of the tool
91      * (mch) not entirely happy with this following changes to Query that include
92      * the result spec.  I've set it to table here and hope that targets etc get
93      * set properly later (as it would have done before)
94      * FURTHER COMMENT BY KEA:  Explicitly setting the ReturnSpec in the 
95      * CONE_IFACE query now, to a ReturnTable.
96      * @param t
97      * @return
98      */
99     protected final Query buildQuery(ApplicationInterface interf)  throws IOException, CeaException, QueryException {
100 
101       if (interf.getName().equals(DatacenterApplicationDescription.CONE_IFACE)) {
102         /*
103          return SimpleQueryMaker.makeConeQuery(
104             Double.parseDouble((String)findInputParameterAdapter(DatacenterApplicationDescription.RA).process())
105                , Double.parseDouble((String)findInputParameterAdapter(DatacenterApplicationDescription.DEC).process())
106                , Double.parseDouble((String)findInputParameterAdapter(DatacenterApplicationDescription.RADIUS).process())
107          );
108          */
109          return new Query(
110             Double.parseDouble((String)findInputParameterAdapter(DatacenterApplicationDescription.RA).process()),
111             Double.parseDouble((String)findInputParameterAdapter(DatacenterApplicationDescription.DEC).process()),
112             Double.parseDouble((String)findInputParameterAdapter(DatacenterApplicationDescription.RADIUS).process()),
113             new ReturnTable(new WriterTarget(new StringWriter()))
114          );
115       }
116       else if (interf.getName().equals(DatacenterApplicationDescription.ADQL_IFACE)) {
117          String querySource = findInputParameter(DatacenterApplicationDescription.QUERY).getValue();
118          String queryString = (String)findInputParameterAdapter(DatacenterApplicationDescription.QUERY).process();
119          if ((queryString == null) || (queryString.trim().length() == 0)) {
120             throw new IOException("Read empty string at "+querySource);
121          }
122          logger.debug("Query will be " + queryString);
123          return new Query(queryString);
124       }
125       else
126       {
127          // shouldn't get here - as would have barfed during 'initializeApplication' in DatacenterApplicatonDescription
128          logger.fatal("Programming logic error - unknown interface" + interf.getName());
129          throw new IllegalArgumentException("Programming logic error - unknown interface" + interf.getName());
130       }
131       
132    }
133    
134    
135    /***
136     * @see org.astrogrid.applications.Application#execute()
137     */
138    public boolean execute() throws CeaException {
139       createAdapters();
140       try {
141          ParameterValue resultTarget = findOutputParameter(DatacenterApplicationDescription.RESULT);
142          TargetIdentifier ti = null;
143          if (resultTarget.getIndirect()==true) {
144             //results will go to the URI given in the parameter
145             if ((resultTarget.getValue() == null) || (resultTarget.getValue().trim().length() == 0)) {
146                throw new CeaException("ResultTarget is indirect but value is empty");
147             }
148             
149             //special botch for converting ivo:// to homespace:// etc
150             String targetUri = transformTarget(resultTarget.getValue());
151             
152             ti = URISourceTargetMaker.makeSourceTarget(targetUri);
153          } else {
154             //direct-to-cea target, so results must get written to a string to be inserted into the
155             //parametervalue when complete.  See queryStatusChanged for what happens to the results
156             //when the query is complete
157             ti = new WriterTarget(new StringWriter(), true); //close stream when finished writing to it
158          }
159 
160          String resultsFormat = MimeNames.getMimeType( findInputParameterAdapter(DatacenterApplicationDescription.FORMAT).process().toString());
161          
162          Query query = buildQuery(getApplicationInterface());
163          query.getResultsDef().setTarget(ti);
164          query.getResultsDef().setFormat(resultsFormat);
165          Querier querier = Querier.makeQuerier(acc,query, "CEA from "+AxisDataServer.getSource()+" [Job "+ids.getJobStepId()+"]");
166          querier.addListener(this);
167          
168          setStatus(Status.INITIALIZED);
169          querierID = serv.submitQuerier(querier);
170          logger.info("assigned QuerierID " + querierID);
171          return true;
172       }
173       catch (Throwable e) {
174          reportError(e+" executing "+this.getTool().getName(),e);
175          if (e instanceof CeaException) {
176             throw (CeaException) e;
177          }
178          else {
179             throw new CeaException(e+", executing application "+this.getTool().getName(),e);
180          }
181       }
182    }
183 
184    /***
185     * If the incoming URI describing the target location is an IVORN, it might be
186     * an account or it might be a real IVORN.  Examines it to see if it is of the
187     * form of an account (ie ivo://community/individual) and if so, checks to see
188     * if the Registry can resolve it.  If it is of the right form, and the registry
189     * cannot resolve it, turns it into a homespacename
190     */
191    public String transformTarget(String targetUri) throws IllegalArgumentException, URISyntaxException {
192       
193       int hashIdx = targetUri.indexOf("#");
194       String key = targetUri.substring(0,hashIdx).substring(6);
195       int slashIdx = key.indexOf("/");
196 
197       /* just assume it's a homespace ivorn for now
198       
199       if (!IVORN.isIvorn(targetUri)) { return targetUri; } //some other URI
200 
201       int hashIdx = targetUri.indexOf("#");
202       if (hashIdx == -1) {
203          throw new IllegalArgumentException("Target URI "+targetUri+" has no path given");
204       }
205       String key = targetUri.substring(0,hashIdx).substring(6);
206       int slashIdx = key.indexOf("/");
207       if ((slashIdx != -1) && (slashIdx == key.lastIndexOf("/"))) { //if there is only one slash
208          
209          //check it can't be resolved
210          try {
211             new IVORN(targetUri).resolve();
212             //it resolved OK, so it's a real IVORN.  Leave unchanged
213             return targetUri;
214          }
215          catch (IOException rre) { } //carry on, it's not resolvable (normally), so...
216        */
217          //assume any exception means it can't be found, so it must be a 'homespace' id.  Can't do much better than that at the mo anyway
218          HomespaceName homespace = HomespaceName.fromIvorn(targetUri);
219          logger.info("Converting incoming '"+targetUri+"' to '"+homespace+"'");
220          return homespace.toURI();
221 //      }
222 //      return targetUri;
223    }
224          
225    /*** Implemented by calling abot on the querier object - so if the underlyng database back end supports abort, the cec does too
226     * @see org.astrogrid.applications.Application#attemptAbort()
227     */
228    public boolean attemptAbort() {
229       try {
230          QuerierStatus stat = serv.abortQuery(acc,querierID);
231          if (stat.getState().equals(QueryState.ABORTED)) {
232             return true;
233          } else {
234             return false; // assume this means we couldn't abort.
235          }
236       } catch (IOException e) {
237          logger.warn("Attempted abort failed with exception",e);
238          return false;
239       }
240    }
241    
242    
243    /*** callback method for our associated querier
244     * <p />Through this method, this class receives notifications in changes of state of the running query.
245     * These state changes and messages are propagated onto the cea framework,
246     * and results are grabbed from the query at the appropriate time.
247     * @see org.astrogrid.datacenter.queriers.QuerierListener#queryStatusChanged(org.astrogrid.datacenter.queriers.Querier)
248     */
249    public void queryStatusChanged(final Querier querier) {
250       QuerierStatus qs = querier.getStatus();
251       QueryState state = qs.getState();
252       logger.debug("CEA seen DSA state="+state);
253       // would be nice to use a switch here, but can't.
254       if (state.equals(QueryState.CONSTRUCTED) || state.equals(QueryState.QUEUED)) {
255          this.setStatus(Status.INITIALIZED);
256          
257       } else if (state.equals(QueryState.STARTING) || state.equals(QueryState.RUNNING_QUERY)) {
258          this.setStatus(Status.RUNNING);
259          
260       } else if (state.equals(QueryState.QUERY_COMPLETE)) {
261          this.reportMessage(qs.toString());
262          
263       } else if (state.equals(QueryState.RUNNING_RESULTS)) {
264          this.setStatus(Status.WRITINGBACK);
265       } else if (state.equals(QueryState.ABORTED)) {
266          this.reportMessage(qs.toString());
267          this.setStatus(Status.ERROR); // this is the convention.
268          
269       } else if (state.equals(QueryState.ERROR)) {
270          this.reportError(qs.toString()); // sets us in error state.
271          
272       } else if (state.equals(QueryState.FINISHED)) {
273          ParameterValue result = findOutputParameter(DatacenterApplicationDescription.RESULT);
274          if (result.getIndirect() != true) {
275             //if the results were to be directed to CEA, they will be stored in a StringWriter in
276             //the WriterTarget
277             WriterTarget target = (WriterTarget) querier.getQuery().getTarget();
278             StringWriter sw = (StringWriter) target.openWriter();
279             result.setValue(sw.toString() );
280          }
281 
282          this.setStatus(Status.COMPLETED);
283          this.reportMessage(qs.toString());
284          
285       } else if (state.equals(QueryState.UNKNOWN)) {
286          this.setStatus(Status.UNKNOWN);
287          this.reportMessage(qs.toString());
288          
289       } else {
290          logger.fatal("Programming error - unknown state encountered" + state.toString());
291          // would like to throw, but won't - as don't know who called me.
292          this.setStatus(Status.UNKNOWN);
293          this.reportMessage("Unkown state encountered " + state.toString());
294       }
295       
296    }
297    
298    
299    /*** overridden, to return instances of datacenter parameter adapter.
300     * @see org.astrogrid.applications.AbstractApplication#instantiateAdapter(org.astrogrid.applications.beans.v1.parameters.ParameterValue, org.astrogrid.applications.description.ParameterDescription, org.astrogrid.applications.parameter.indirect.IndirectParameterValue)
301     *
302    protected ParameterAdapter instantiateAdapter(ParameterValue arg0,
303                                                  ParameterDescription arg1, ExternalValue arg2) {
304       return new DatacenterParameterAdapter(arg0, arg1, arg2);
305    }
306     */
307 }
308 
309 
310 /*
311  $Log: DatacenterApplication.java,v $
312  Revision 1.4  2006/06/15 16:50:08  clq2
313  PAL_KEA_1612
314 
315  Revision 1.3.64.3  2006/05/10 13:25:22  kea
316  Conesearch and HSQLDB fixes/enhancements;  moving properties to web.xml
317  like other AG services.
318 
319  Revision 1.3.64.2  2006/04/21 11:54:05  kea
320  Changed QueryException from a RuntimeException to an Exception.
321 
322  Revision 1.3.64.1  2006/04/19 13:57:31  kea
323  Interim checkin.  All source is now compiling, using the new Query model
324  where possible (some legacy classes are still using OldQuery).  Unit
325  tests are broken.  Next step is to move the legacy classes sideways out
326  of the active tree.
327 
328  Revision 1.3  2005/05/27 16:21:08  clq2
329  mchv_1
330 
331  Revision 1.2.16.4  2005/05/16 14:10:54  mch
332  use new fromIvorn method
333 
334  Revision 1.2.16.3  2005/05/13 16:56:31  mch
335  'some changes'
336 
337  Revision 1.2.16.2  2005/04/25 14:41:14  mch
338  better comment
339 
340  Revision 1.2.16.1  2005/04/21 17:20:51  mch
341  Fixes to output types
342 
343  Revision 1.2  2005/03/21 18:45:55  mch
344  Naughty big lump of changes
345 
346  Revision 1.1.1.1  2005/02/17 18:37:35  mch
347  Initial checkin
348 
349  Revision 1.1.1.1  2005/02/16 17:11:24  mch
350  Initial checkin
351 
352  Revision 1.12.2.11  2005/02/11 15:58:27  mch
353  Some fixes before split
354 
355  Revision 1.12.2.10  2004/12/08 18:36:40  mch
356  Added Vizier, rationalised SqlWriters etc, separated out TableResults from QueryResults
357 
358  Revision 1.12.2.9  2004/12/07 21:21:09  mch
359  Fixes after a days integration testing
360 
361  Revision 1.12.2.8  2004/12/07 00:02:17  mch
362  added tentative ivorn->homespace converter
363 
364  Revision 1.12.2.7  2004/11/30 01:04:02  mch
365  Rationalised tablewriters, reverted AxisDataService06 to string
366 
367  Revision 1.12.2.6  2004/11/29 21:52:18  mch
368  Fixes to skynode, log.error(), getstem, status logger, etc following tests on grendel
369 
370  Revision 1.12.2.5  2004/11/25 08:29:41  mch
371  added table writers modelled on STIL
372 
373  Revision 1.12.2.4  2004/11/23 17:46:52  mch
374  Fixes etc
375 
376  Revision 1.12.2.3  2004/11/23 12:34:01  mch
377  renamed to makeTarget
378 
379  Revision 1.12.2.2  2004/11/23 11:55:06  mch
380  renamved makeTarget methods
381 
382  Revision 1.12.2.1  2004/11/22 00:57:16  mch
383  New interfaces for SIAP etc and new slinger package
384 
385  Revision 1.12  2004/11/11 20:42:50  mch
386  Fixes to Vizier plugin, introduced SkyNode, started SssImagePlugin
387 
388  Revision 1.11  2004/11/10 22:01:50  mch
389  skynode starts and some fixes
390 
391  Revision 1.10  2004/11/09 17:42:22  mch
392  Fixes to tests after fixes for demos, incl adding closable to targetIndicators
393 
394  Revision 1.9  2004/11/08 02:58:44  mch
395  Various fixes and better error messages
396 
397  Revision 1.8  2004/11/03 00:17:56  mch
398  PAL_MCH Candidate 2 merge
399 
400  Revision 1.3.8.4  2004/11/02 21:51:54  mch
401  Replaced AgslTarget with UrlTarget and MySpaceTarget
402 
403  Revision 1.3.8.3  2004/11/02 19:48:43  mch
404  Split TargetIndicator to indicator and maker
405 
406  Revision 1.3.8.2  2004/11/02 19:41:26  mch
407  Split TargetIndicator to indicator and maker
408 
409  Revision 1.3.8.1  2004/10/20 18:12:45  mch
410  CEA fixes, resource tests and fixes, minor navigation changes
411 
412  Revision 1.4.2.1  2004/10/20 12:43:28  mch
413  Fixes to CEA interface to write directly to target
414 
415  Revision 1.4  2004/10/20 08:10:55  pah
416  taken Principal of new backend phase and tidied up some logging
417 
418  Revision 1.3  2004/10/07 10:34:44  mch
419  Fixes to Cone maker functions and reading/writing String comparisons from Query
420 
421  Revision 1.2  2004/10/06 21:12:17  mch
422  Big Lump of changes to pass Query OM around instead of Query subclasses, and TargetIndicator mixed into Slinger
423 
424  Revision 1.1  2004/09/28 15:02:13  mch
425  Merged PAL and server packages
426 
427  Revision 1.8  2004/09/17 01:27:21  nw
428  added thread management.
429 
430  Revision 1.7  2004/09/06 15:20:47  mch
431  Added logger message for execute()
432 
433  Revision 1.6  2004/08/25 23:38:34  mch
434  (Days changes) moved many query- and results- related classes, renamed packages, added tests, added CIRCLE to sql/adql parsers
435 
436  Revision 1.5  2004/08/17 20:19:36  mch
437  Moved TargetIndicator to client
438 
439  Revision 1.4  2004/07/27 13:48:33  nw
440  renamed indirect package to protocol,
441  renamed classes and methods within protocol package
442 
443  Revision 1.3  2004/07/22 16:31:22  nw
444  cleaned up application / parameter adapter interface.
445 
446  Revision 1.2  2004/07/20 02:14:48  nw
447  final implementaiton of itn06 Datacenter CEA interface
448 
449  Revision 1.1  2004/07/13 17:11:09  nw
450  first draft of an itn06 CEA implementation for datacenter
451  
452  */
453