View Javadoc

1   /*
2    * $Id: Querier.java,v 1.13 2004/11/11 20:42:50 mch Exp $
3    *
4    * (C) Copyright Astrogrid...
5    */
6   
7   package org.astrogrid.datacenter.queriers;
8   
9   import org.astrogrid.datacenter.queriers.status.*;
10  
11  import java.io.IOException;
12  import java.util.Date;
13  import java.util.Vector;
14  import org.apache.commons.logging.Log;
15  import org.astrogrid.community.Account;
16  import org.astrogrid.datacenter.delegate.DatacenterException;
17  import org.astrogrid.datacenter.query.Query;
18  import org.astrogrid.datacenter.slinger.Slinger;
19  
20  /***
21   * Represents a single running query.
22   *
23   * <p>
24   * If two queries will be made on a database, two Querier instances
25   * will be required.
26   * <p>
27   * It is a very abstract class meant to help administer
28   * the queries - concrete subclasses take care of the details of performing the query.
29   * <p>
30   * @see package documentation
31   * <p>
32   * This class provides factory methods for both blocking and non-blocking
33   * queries, hopefully completely hiding the two-way translation process
34   *
35   * @see doQueryGetVotable()
36   * @see spawnQuery
37   * <p>
38   * @author M Hill
39   */
40  
41  public class Querier implements Runnable, PluginListener {
42     
43     
44     protected static final Log log = org.apache.commons.logging.LogFactory.getLog(Querier.class);
45         
46     /*** query to perform */
47     private final Query query;
48     
49     /*** A handle is used to identify a particular query.  It is also used as the
50      * basis for any temporary storage. */
51     private final String id;
52     
53     /*** On whose behalf is this querier running */
54     private final Account user;
55  
56     /*** Plugin to run */
57     private QuerierPlugin plugin;
58     
59     /*** List of listeners who will be updated when the status changes */
60     private Vector listeners = new Vector();
61     
62     /*** status of query */
63     private QuerierStatus status;
64  
65     /*** true if abort called */
66     private boolean aborted = false;
67     
68     /*** Represents what created this querier */
69     private Object source = null;
70     
71     /*** For measuring how long the query took - calculated from status change times*/
72  //use status info   private Date timeQueryStarted = null;
73     /*** For measuring how long query took - calculated from status change times*/
74  //use status info   private Date timeQueryCompleted = null;
75     /*** For measuring how long query took - calculated from status change times*/
76     //use status infoprivate Date timeQuerierClosed = null;
77  
78     /*** temporary used for generating unique handles - see generateHandle() */
79     private static java.util.Random random = new java.util.Random();
80  
81  
82     /*** This is private so that if the mechanism changes and we make the plugins
83      subclasses of queriers, for example, then we don't have to change the rest of the
84      * code.  The query includes details about the results, and the 'aSource' is just
85      * used to indicate where the querier came from - eg a test class, or JSPs, or
86      * CEA, etc */
87     private Querier(Account forUser, Query query, Object aSource) throws IOException {
88        this.id = generateQueryId();
89        this.user = forUser;
90        this.query = query;
91        this.source = aSource;
92        
93        //make plugin
94        plugin = QuerierPluginFactory.createPlugin(this);
95        
96        setStatus(new QuerierConstructed(this));
97        if (source != null) {
98           status.addDetail("Source: "+source.toString());
99        }
100 
101       //do this as part of the constructor so that we get errors back even on
102       //submitted (asynchronous) queries.
103       
104       if (query.getResultsDef() != null) //some things like askCount have no results definition
105       {
106          Slinger.testConnection(query.getTarget(), user);
107       }
108 
109       
110    }
111    
112    /*** Factory method.  Query includes the results definition, and 'source' represents
113     * what clas was used to create this querier (optional) */
114    public static Querier makeQuerier(Account forUser, Query query, Object source) throws IOException {
115       if (source != null) {
116          source = source.getClass();
117       }
118       
119       Querier querier = new Querier(forUser, query, source);
120 
121       return querier;
122    }
123 
124    /*** Returns this instances handle    */
125    public String getId() {       return id;   }
126 
127    /*** Returns the query for subclasses */
128    public Query getQuery() { return query; }
129    
130     /*** Returns the account the querier is being run for  */
131    public Account getUser() { return user; }
132    
133    /*** Returns the plugin - this is *only* for testing @todo a nicer way of doing this */
134    public QuerierPlugin getPlugin() { return plugin; }
135    
136    /*** Returns the class involved in creating this querier instance.  Used for
137     * analysis etc */
138    public Object getSource() { return source; }
139    
140    /***
141     * Runnable implementation - this method is called when the thread to run
142     * this asynchronously is started.  It should just do ask() and handle
143     * any error, because these won't go anywhere otherwise...
144     */
145    public void run() {
146       log.info("Starting Query ["+id+"] asynchronously...");
147       
148       try {
149          ask();
150       }
151       catch (Throwable th) {
152          log.error("Exception during Asynchronous Run",th);
153          if (!(getStatus() instanceof QuerierError)) {
154             setStatus(new QuerierError(getStatus(), "", th));
155          }
156       }
157       log.info("...Ending asynchronous Query ["+id+"]");
158       
159    }
160 
161    /*** Carries out the query via the plugin to do the query.
162     * The plugin does the complete conversion, query and
163     * results processing
164      */
165    public void ask() throws IOException {
166       
167       //by this stage a target should have been specified
168       if ((query.getResultsDef() == null) || (query.getTarget() == null)) {
169          throw new DatacenterException("No taret given for the results");
170       }
171  //     plugin = QuerierPluginFactory.createPlugin(this);
172       
173       if (!(getStatus() instanceof QuerierAborted)) { // it may be that the query has been aborted immediately after being created, or while queued, etc
174          plugin.askQuery(user, query, this);
175       
176          close();
177       }
178    }
179   
180    /*** Asks the plugin for the count (ie number of matches) for
181     * the given query.  These are asynchronous (blocking)
182     */
183    public long askCount() throws IOException {
184       return plugin.getCount(user, query, this);
185    }
186    
187 
188    /***
189     * Closes & tidies up
190     */
191    public void close() {
192       if (!getStatus().isFinished()) {
193          setStatus(new QuerierComplete(getStatus()));
194       }
195       plugin = null;  //release plugin reference (-> can be garbage collected)
196    }
197    
198    /***
199     * Returns the time it took to complete the query in milliseconds, or the
200     * time since it started (if it's still running).  -1 if the query has not
201     * yet started
202     */
203    public long getQueryTimeTaken() {
204 
205       //look for QuerierQuerying status
206       QuerierStatus status = getStatus();
207       QuerierStatus next = null;
208       while ((status != null) && !(status instanceof QuerierQuerying))  {
209          next = status;
210          status = status.getPrevious();
211       }
212       if (status == null) {
213          //hasn't started yet
214          return -1;
215       }
216       Date queryStarted = status.getTimestamp();
217       if (next != null) {
218          //the next status timestamp gives us the query completion time
219          return next.getTimestamp().getTime() - queryStarted.getTime();
220       }
221       else {
222          return new Date().getTime() - queryStarted.getTime();
223       }
224    }
225    
226    /***
227     * Abort - stops query (if poss) and tidies up
228     */
229    public QuerierStatus abort() {
230 
231       //if it's already completed stopped, plugin will be null
232       if (plugin != null) {
233          plugin.abort();
234          setStatus(new QuerierAborted(getStatus()));
235       }
236          
237       aborted = true;
238       
239       return getStatus();
240          
241    }
242    
243    /***
244     * For debugging/display
245     */
246    public String toString() {
247       return "Querier ["+getId()+"] ";
248    }
249 
250    /***
251     * Returns if the querier is closed and no more operations are possible
252     */
253    public boolean isClosed() {
254       return (status.isFinished());
255    }
256    
257    /*** Called by the plugin to register a status change
258     */
259    public void pluginStatusChanged(QuerierStatus newStatus) {
260       setStatus(newStatus);
261    }
262    
263    
264    
265    /***
266     * Sets the status.  NB if the new status is ordered before the existing one,
267     * throws an exception (as each querier should only handle one query).
268     * Synchronised as the queriers may be running under a different thread
269     */
270    public synchronized void setStatus(QuerierStatus newStatus) {
271 
272       if (status != null) {
273          if ((status instanceof QuerierError) || (newStatus.isBefore(status))) {
274             String msg = "Trying to start a step '"+newStatus+"' when status is already "+status;
275             log.error(msg);
276             throw new IllegalStateException(msg);
277          }
278       }
279       
280       log.info("Query ["+id+"] for "+user+", now "+newStatus);
281          
282       status = newStatus;
283       
284       fireStatusChanged(status);
285    }
286    
287    /***
288     * Returns the status - contains more info than the state
289     */
290    public QuerierStatus getStatus() {
291       return status;
292    }
293    
294    /***
295     * Register a status listener.  This will be informed of changes in status
296     * to the service - IF the service supports such info.  Otherwise it will
297     * just get 'starting', 'working' and 'completed' messages based around the
298     * basic http exchange.
299     */
300    public void addListener(QuerierListener aListener) {
301       listeners.add(aListener);
302    }
303 
304    /***
305     * Removes a listern
306     */
307    public void removeListener(QuerierListener aListener) {
308       listeners.remove(aListener);
309    }
310    /*** informs all listeners of the new status change. Not threadsafe... should
311     * call setStatus() rather than this directly
312     */
313    private void fireStatusChanged(QuerierStatus newStatus) {
314       for (int i=0;i<listeners.size();i++) {
315          try {
316             ((QuerierListener) listeners.get(i)).queryStatusChanged(this);
317          }
318          catch (RuntimeException e) {
319             //if there is a problem informing a listener, log it as an error but
320             //get on with the query
321             log.error("Listener ("+listeners.get(i)+") failed to handle status change to "+newStatus, e);
322             
323          }
324       }
325    }
326 
327    
328    /***
329     * Helper method to generates a handle for use by a particular instance; uses the current
330     * time to help us debug (ie we can look at the temporary directories and
331     * see which was the last run). Later we could add service/user information
332     * if available
333     * @todo not guaranteed to be unique...
334     */
335    protected static String generateQueryId() {
336       Date todayNow = new Date();
337        return
338          todayNow.getYear()
339          + "-"
340          + todayNow.getMonth()
341          + "-"
342          + todayNow.getDate()
343          + "_"
344          + todayNow.getHours()
345          + "."
346          + todayNow.getMinutes()
347          + "."
348          + todayNow.getSeconds()
349          + "_"
350          //plus botched bit... not really unique
351          + (random.nextInt(8999999) + 1000000);
352       
353    }
354    
355 
356    
357 }
358 /*
359  $Log: Querier.java,v $
360  Revision 1.13  2004/11/11 20:42:50  mch
361  Fixes to Vizier plugin, introduced SkyNode, started SssImagePlugin
362 
363  Revision 1.12  2004/11/10 22:01:50  mch
364  skynode starts and some fixes
365 
366  Revision 1.11  2004/11/09 18:27:21  mch
367  added askCount
368 
369  Revision 1.10  2004/11/03 00:17:56  mch
370  PAL_MCH Candidate 2 merge
371 
372 
373  Revision 1.6.6.3  2004/11/02 19:41:26  mch
374  Split TargetIndicator to indicator and maker
375 
376  Revision 1.6.6.2  2004/11/01 16:01:25  mch
377  removed unnecessary getLocalLimit parameter, and added check for abort in sqlResults
378 
379  Revision 1.6.6.1  2004/10/20 18:12:45  mch
380  CEA fixes, resource tests and fixes, minor navigation changes
381 
382  Revision 1.6.8.1  2004/10/20 12:43:28  mch
383  Fixes to CEA interface to write directly to target
384 
385  Revision 1.6  2004/10/18 13:11:30  mch
386  Lumpy Merge
387 
388  Revision 1.5.2.1  2004/10/15 19:59:05  mch
389  Lots of changes during trip to CDS to improve int test pass rate
390 
391  Revision 1.5  2004/10/07 10:34:44  mch
392  Fixes to Cone maker functions and reading/writing String comparisons from Query
393 
394  Revision 1.4  2004/10/06 21:12:17  mch
395  Big Lump of changes to pass Query OM around instead of Query subclasses, and TargetIndicator mixed into Slinger
396 
397  Revision 1.3  2004/10/05 15:04:28  mch
398  Get time taken from status now
399 
400  Revision 1.2  2004/10/01 18:04:58  mch
401  Some factoring out of status stuff, added monitor page
402 
403  Revision 1.1  2004/09/28 15:02:13  mch
404  Merged PAL and server packages
405 
406  Revision 1.60  2004/09/07 01:03:10  mch
407  Moved testConnection to server slinger
408 
409  Revision 1.59  2004/09/07 00:54:20  mch
410  Tidied up Querier/Plugin/Results, and removed deprecated SPI-visitor-SQL-translator
411 
412  Revision 1.58  2004/08/27 17:47:19  mch
413  Added first servlet; started making more use of ReturnSpec
414 
415  Revision 1.57  2004/08/25 23:38:34  mch
416  (Days changes) moved many query- and results- related classes, renamed packages, added tests, added CIRCLE to sql/adql parsers
417 
418  Revision 1.56  2004/08/18 21:35:50  mch
419  Fixed odd bug; plugin created in constructor was lost, and created again during ask
420 
421  Revision 1.55  2004/08/17 20:19:36  mch
422  Moved TargetIndicator to client
423 
424  Revision 1.54  2004/07/06 18:48:34  mch
425  Series of unit test fixes
426 
427  Revision 1.53  2004/07/05 16:35:00  mch
428  Added minor doc
429 
430  Revision 1.52  2004/07/05 16:34:09  mch
431  Fix to close if no plugin given
432 
433  Revision 1.51  2004/04/01 17:14:59  mch
434  Attempt to remove plugin on close
435 
436  Revision 1.50  2004/03/29 16:58:15  mch
437  Fix for plugin instance variable not being set
438 
439  Revision 1.49  2004/03/18 23:41:32  mch
440  Reintroduced more thorough pre-query storepoint test
441 
442  Revision 1.48  2004/03/18 20:42:37  mch
443  Removed test myspace
444 
445  Revision 1.47  2004/03/17 12:53:44  mch
446  Fixed QuerierStatus being gotten from somewhere else
447 
448  Revision 1.46  2004/03/16 17:41:14  mch
449  set querier error status only if not already set in run
450 
451  Revision 1.45  2004/03/15 19:16:12  mch
452  Lots of fixes to status updates
453 
454  Revision 1.44  2004/03/15 17:08:52  mch
455  Added status detail copies
456 
457  Revision 1.43  2004/03/15 11:25:35  mch
458  Fixes to emailer and JSP targetting
459 
460  Revision 1.42  2004/03/14 04:13:04  mch
461  Wrapped output target in TargetIndicator
462 
463  Revision 1.41  2004/03/14 02:17:07  mch
464  Added CVS format and emailer
465 
466  Revision 1.40  2004/03/14 01:10:00  mch
467  Defaults to VOTable
468 
469  Revision 1.39  2004/03/14 00:39:55  mch
470  Added error trapping to DataServer and setting Querier error status
471 
472  Revision 1.38  2004/03/13 23:38:46  mch
473  Test fixes and better front-end JSP access
474 
475  Revision 1.37  2004/03/13 16:26:40  mch
476  Changed makeFullSearcher to makeQuerySearcher
477 
478  Revision 1.36  2004/03/12 20:04:57  mch
479  It05 Refactor (Client)
480 
481  Revision 1.35  2004/03/12 05:15:32  mch
482  Removed automatic send to myspace
483 
484  Revision 1.34  2004/03/12 05:12:29  mch
485  Made sure failed delete doens't stop query
486 
487  Revision 1.33  2004/03/12 04:45:26  mch
488  It05 MCH Refactor
489 
490  Revision 1.32  2004/03/09 22:58:39  mch
491  Provided for piping/writing out of results rather than returning as string
492 
493  Revision 1.31  2004/03/09 21:04:30  mch
494  minor doc change
495 
496  Revision 1.30  2004/03/09 19:08:54  mch
497  Added debug to results destination
498 
499  Revision 1.29  2004/03/08 00:31:28  mch
500  Split out webservice implementations for versioning
501 
502  Revision 1.28  2004/03/07 00:33:50  mch
503  Started to separate It4.1 interface from general server services
504 
505  Revision 1.27  2004/03/06 19:34:21  mch
506  Merged in mostly support code (eg web query form) changes
507 
508  Revision 1.25  2004/03/02 01:37:20  mch
509  Updates from changes to StoreClient and AGSLs
510 
511  Revision 1.24  2004/02/24 19:12:39  mch
512  Added logging info trace
513 
514  Revision 1.23  2004/02/24 16:13:23  mch
515  fix to allow no DefaultMySpace
516 
517  Revision 1.22  2004/02/24 16:04:18  mch
518  Config refactoring and moved datacenter It04.1 VoSpaceStuff to myspace StoreStuff
519 
520  Revision 1.21  2004/02/24 11:03:03  mch
521  Temp fix to ignore Query.Unit
522 
523  Revision 1.20  2004/02/17 03:38:05  mch
524  Various fixes for demo
525 
526  Revision 1.19  2004/02/16 23:34:35  mch
527  Changed to use Account and AttomConfig
528 
529  Revision 1.18  2004/02/15 23:17:05  mch
530  Naughty Big Lump of Changes cont: fixes for It04.1 myspace
531 
532  Revision 1.17  2004/01/16 13:14:07  nw
533  initialized resultDestination to default myspace given in config.
534  previously wasn't being initialized.
535 
536  Revision 1.16  2004/01/15 17:38:25  nw
537  adjusted how queriers close() themselves - altered so it
538  works no matter if Querier.close() or QuerierManager.closeQuerier(q)
539  is called.
540 
541  Revision 1.15  2004/01/15 14:49:47  nw
542  improved documentation
543 
544  Revision 1.14  2004/01/14 17:57:32  nw
545  improved documentation
546 
547  Revision 1.13  2004/01/13 00:33:14  nw
548  Merged in branch providing
549  * sql pass-through
550  * replace Certification by User
551  * Rename _query as Query
552 
553  Revision 1.12  2004/01/12 15:08:14  mch
554  Fix to record url of results not content of results...
555 
556  Revision 1.11.4.3  2004/01/08 09:43:41  nw
557  replaced adql front end with a generalized front end that accepts
558  a range of query languages (pass-thru sql at the moment)
559 
560  Revision 1.11.4.2  2004/01/07 13:02:09  nw
561  removed Community object, now using User object from common
562 
563  Revision 1.11.4.1  2004/01/07 11:51:07  nw
564  found out how to get wsdl to generate nice java class names.
565  Replaced _query with Query throughout sources.
566 
567  Revision 1.11  2003/12/15 17:47:55  mch
568  Introduced proper MySpace Factory
569 
570  Revision 1.10  2003/12/03 19:37:03  mch
571  Introduced DirectDelegate, fixed DummyQuerier
572 
573  Revision 1.9  2003/12/02 17:57:35  mch
574  Moved MySpaceDummyDelegate
575 
576  Revision 1.8  2003/12/01 20:57:39  mch
577  Abstracting coarse-grained plugin
578 
579  Revision 1.7  2003/12/01 16:43:52  nw
580  dropped QueryId, back to string
581 
582  Revision 1.6  2003/12/01 16:11:30  nw
583  removed config interface.
584 
585  Revision 1.5  2003/11/28 16:10:30  nw
586  finished plugin-rewrite.
587  added tests to cover plugin system.
588  cleaned up querier & queriermanager. tested
589 
590  Revision 1.4  2003/11/27 17:28:09  nw
591  finished plugin-refactoring
592 
593  Revision 1.3  2003/11/27 00:52:58  nw
594  refactored to introduce plugin-back end and translator maps.
595  interfaces in place. still broken code in places.
596 
597  Revision 1.2  2003/11/25 18:50:06  mch
598  Abstracted Querier from DatabaseQuerier
599 
600  Revision 1.1  2003/11/25 14:17:24  mch
601  Extracting Querier from DatabaseQuerier to handle non-database backends
602 
603  Revision 1.7  2003/11/25 11:57:32  mch
604  Added framework for SQL-passthrough queries
605 
606  Revision 1.6  2003/11/21 17:37:56  nw
607  made a start tidying up the server.
608  reduced the number of failing tests
609  found commented out code
610 
611  Revision 1.5  2003/11/18 14:36:21  nw
612  temporarily commented out references to MySpaceDummyDelegate, so that the sustem will build
613 
614  Revision 1.4  2003/11/18 11:10:16  mch
615  Removed client dependencies on server
616 
617  Revision 1.3  2003/11/17 15:41:48  mch
618  Package movements
619 
620  Revision 1.2  2003/11/17 12:16:33  nw
621  first stab at mavenizing the subprojects.
622 
623  Revision 1.1  2003/11/14 00:38:29  mch
624  Code restructure
625 
626  Revision 1.37  2003/11/06 22:06:00  mch
627  Reintroduced credentials (removed them due to out of date myspace delegate)
628 
629  Revision 1.36  2003/11/06 11:38:48  mch
630  Introducing SOAPy Beans
631 
632  */
633 
634 
635 
636 
637