View Javadoc

1   /*
2    * $Id: WarehouseQuerier.java,v 1.6 2004/11/22 14:43:21 jdt Exp $
3    *
4    * (C) Copyright Astrogrid...
5    */
6   
7   package org.astrogrid.datacenter.queriers.ogsadai;
8   
9   import java.io.File;
10  import java.io.IOException;
11  import org.astrogrid.community.Account;
12  import org.astrogrid.config.SimpleConfig;
13  import org.astrogrid.datacenter.queriers.DatabaseAccessException;
14  import org.astrogrid.datacenter.queriers.DefaultPlugin;
15  import org.astrogrid.datacenter.queriers.Querier;
16  import org.astrogrid.datacenter.queriers.VotableInResults;
17  import org.astrogrid.datacenter.queriers.sql.postgres.PostgresSqlMaker;
18  import org.astrogrid.datacenter.queriers.status.QuerierQuerying;
19  import org.astrogrid.datacenter.query.Query;
20  import org.astrogrid.util.Workspace;
21  
22  /***
23   * An AstroGrid datacenter plugin Querier that provides access
24   * to the AstroGrid OGSA-DAI Grid Data Warehouse.
25   *
26   * Due to incompatibilities between vanilla Axis and the customised
27   * version of Axis used by OGSA-DAI, this plugin querier does not talk
28   * directly to the OGSA-DAI installation.  (This allows us to insulate
29   * the datacenter runtime from the customised Axis classes).
30   *
31   * Instead, it shells out to the command-line and invokes a
32   * {@link GdsQueryDelegate} (running in a new JVM) to perform
33   * the query.  The results supplied by the OGSA-DAI service
34   * in VOTable format.
35   *
36   * Communication between this <code>WarehouseQuerier</code> and
37   * a {@link GdsQueryDelegate} is via process input and output streams.
38   * This version of the WarehouseQuerier requests OGSA-DAI to deliver
39   * its results to a local file. The implications of this are:
40   * <ul>
41   * <li> The tomcat instances hosting the OGSA-DAI service and the
42   * Datacenter service <strong>must be running on the same host</strong>.
43   *
44   * <li>The tomcat instances hosting the OGSA-DAI service and the
45   * Datacenter service should probably be running as the same user
46   * (because the latter must create, and the former must write to,
47   * the same temporary file).
48   * </ul>
49   *
50   * Delivery to a GridFTP URL is also supported by the It5 OGSA-DAI client
51   * code, but requires additional support at the datacenter end in terms
52   * of X.509 authentication etc. It is not explicitly supported in this
53   * Iteration 5 version of the WarehouseQuerier, which relies on
54   * deliver-to-file instead.
55   *
56   * @author K Andrews
57   * @version 1.1
58   * @see GdsQueryDelegate
59   */
60  public class WarehouseQuerier extends DefaultPlugin {
61     
62     Workspace workspace = null;
63     
64  
65    /***
66     * Performs an actual database query (by shelling out to a
67     * GdsQueryDelegate running in a separate JVM).
68     *
69     * Converts input ADQL query into the SQL expected by the
70     * GdsQueryDelegate, using a custom Postgres-flavoured translator.
71     *
72     * @throws IOException, DatabaseAccessException
73     */
74     public void askQuery(Account user, Query query, Querier querier) throws IOException {
75  
76        querier.setStatus(new QuerierQuerying(querier.getStatus()));
77  
78      //Convert to SQL
79      PostgresSqlMaker sqlMaker = new PostgresSqlMaker();
80      String sql = escapeXmlSpecialChars(sqlMaker.makeSql(query));
81       
82      log.debug("Successfully created SQL query from input ADQL");
83  
84      // Get temp file to pass to Query delegate
85      try {
86         workspace = new Workspace("Warehouse_"+querier.getId());
87      }
88      catch (Exception th) {
89         log.error(th);
90         throw new IOException(
91            "Couldn't create temporary workspace: " +
92            th.getMessage());
93      }
94       
95      File tempFile = null;
96      //Set up outputs
97      if (this.workspace != null) {
98        try {
99          tempFile = workspace.makeWorkFile(TEMP_RESULTS_FILENAME);
100       }
101       catch (Exception e) { //IOException or FileNotFoundException
102         // Couldn't create temporary workspace file, use stdout instead
103         String errMess = "Couldn't open temporary workspace file: "
104             + e.getMessage();
105         log.error(errMess);
106         throw new DatabaseAccessException(errMess);
107       }
108     }
109     else {
110       log.error("WarehouseQuerier workspace is null");
111       throw new IOException("WarehouseQuerier workspace is null");
112     }
113     doShelledOutQuery(sql, tempFile);
114     VotableInResults results = new VotableInResults(querier, tempFile);
115     results.send(query.getResultsDef(), querier.getUser());
116     workspace.close();
117   }
118 
119    /*** Abort - if this is called, try and kill the query and tidy up.
120     * Currently not implemented.  I don't know how we're going to
121     * do this using the shelled-out query method, yuk!
122     */
123    public void abort()  {
124       //don't forget to workspace.close();
125       // TODO
126    }
127    
128  /***
129    * Shells out to the command line to delegate the query operation to
130    * a GdsQueryDelegate running in a separate JVM.
131    *
132    * The GdsQueryDelegate accepts SQL, performs the actual query via
133    * OGSA-DAI, and returns VOTable results.
134    *
135    * @param sql  String containing the SQL query to be performed
136    * @param tempFile  File to hold the GdsQueryDelegate's VOTable results
137    * @throws DatabaseAccessException
138    */
139   protected void doShelledOutQuery(String sql, File tempFile)
140       throws DatabaseAccessException {
141 
142     if (sql == null) {
143       String errMess = "Empty sql query string supplied to WarehouseQuerier";
144       log.error(errMess);
145       throw new DatabaseAccessException(errMess);
146     }
147     if (tempFile == null) {
148       String errMess = "Null results destination supplied to WarehouseQuerier";
149       log.error(errMess);
150       throw new DatabaseAccessException(errMess);
151     }
152 
153     log.debug("Commencing doShelledOutQuery");
154 
155     // Configure parameters for external call
156     String[] cmdArgs;
157     if (tempFile == null) {
158       cmdArgs = new String[5];
159     }
160     else {
161       cmdArgs = new String[6];
162     }
163     cmdArgs[0] = getJavaBinary();
164     cmdArgs[1] = "-jar";
165     cmdArgs[2] = getExecutableJar();
166     cmdArgs[3] = sql;
167     cmdArgs[4] = getOgsaDaiRegistryString();
168     cmdArgs[5] = "file://" + tempFile.getAbsolutePath();
169     // FUTURE: We can use gsiftp urls here too
170     //cmdArgs[5] = "gsiftp://" + tempFile.getAbsolutePath();
171     log.debug("Command is: " + cmdArgs[0] + " " + cmdArgs[1] +
172           " " + cmdArgs[2] + " " + cmdArgs[3] + " " + cmdArgs[4]);
173 
174     // Use utility helper to perform call
175     log.info("Commencing shelled-out query");
176     SystemTalker talker = new SystemTalker();
177     TalkResult result = talker.talk(cmdArgs, "");
178 
179     if (result.getErrorCode() != 0) {
180       log.error("Shelled-out query failed:" + result.getStderr());
181       throw new DatabaseAccessException(
182         "External call failed: " + result.getStderr());
183     }
184     // Finished external call successfully
185     log.info("Shelled-out query succeeded");
186   }
187 
188   /***
189    * Assembles the URL of the OGSA-DAI registry to be used by the
190    * GdsQueryDelegate.
191    *
192    * @return  String holding full URL of the OGSA-DAI registry
193    * @throws DatabaseAccessException
194    */
195   protected String getOgsaDaiRegistryString() throws DatabaseAccessException {
196     String host = SimpleConfig.getProperty("WAREHOUSE_OgsaDaiHostString");
197     if (host == null) {
198       String errorMessage =
199         "Fatal error: Property 'WAREHOUSE_OgsaDaiHostString' not found " +
200         " in file 'AstroGridConfig.properties'";
201       log.error(errorMessage);
202       throw new DatabaseAccessException(errorMessage);
203     }
204     String registry = SimpleConfig.getProperty("WAREHOUSE_OgsaDaiRegistryString");
205     if (registry == null) {
206       String errorMessage =
207         "Fatal error: Property 'WAREHOUSE_OgsaDaiRegistryString' not found" +
208         " in file 'WarehouseQuerier.properties'";
209       log.error(errorMessage);
210       throw new DatabaseAccessException(errorMessage);
211     }
212     return host + registry;
213   }
214 
215   /***
216    * Assembles the fully-qualified path of the Java JVM to be shelled
217    * out to.
218    *
219    * By default, looks for the environment variable JAVA_HOME and figures
220    * out the path from that.
221    *
222    * If JAVA_HOME is not defined, looks for a local property instead.
223    *
224    * @return  String holding full path of the Java JVM binary
225    * @throws DatabaseAccessException
226    */
227   protected String getJavaBinary() throws DatabaseAccessException {
228     // First, check if user has customised the JVM location
229     //  in the WarehouseQuerier.properties file
230     String customJVM = SimpleConfig.getProperty("WAREHOUSE_WarehouseJvm");
231     if (customJVM != null) {
232       return customJVM;  // Use customised JVM if it exists
233     }
234     else {
235       // No custom JVM - use JVM in JAVA_HOME
236       String javaHome = System.getProperty("java.home");
237       if (javaHome == null) {  //Shouldn't happen
238         String errorMessage =
239           "Fatal error: System property 'java.home' not defined! "+
240              "Please set WAREHOUSE_WarehouseJvm property in " +
241              "'AstroGridConfig.properties' file";
242         log.error(errorMessage);
243         throw new DatabaseAccessException(errorMessage);
244       }
245       String separator = System.getProperty("file.separator");
246       if (separator == null) {
247         log.warn("Warning, couldn't get system file.separator, assuming '/'");
248         separator = "/";
249       }
250       // Assume JVM binary is called java and is in bin dir of JAVA_HOME
251       String fullPath = javaHome + separator + "bin" + separator + "java";
252       File testFile = new File(fullPath);
253       if (!(testFile.exists())) {
254         //Try looking for a .exe version - might be running under Windows
255         testFile = new File(fullPath + ".exe");
256         if (!(testFile.exists())) {
257           String errorMessage =
258             "Fatal error: Java binary '" + fullPath + "[.exe]' not found! "+
259                "Please set WAREHOUSE_WarehouseJvm property in " +
260                "'AstroGridConfig.properties' file";
261           log.error(errorMessage);
262           throw new DatabaseAccessException(errorMessage);
263         }
264         else {
265           return fullPath + ".exe";
266         }
267       }
268       return fullPath;
269     }
270   }
271 
272   /***
273    * Assembles the fully-qualified path of the Java executable jar
274    * containing the OGSA-DAI GdsQueryDelegate.
275    *
276    * The location and name of the jar can be customised for a given
277    * installation in the WarehouseQuerier.properties file.
278    *
279    * @return  String holding full path of the GdsQueryDelegate executable jar
280    * @throws DatabaseAccessException
281    */
282   protected String getExecutableJar() throws DatabaseAccessException {
283     // Extract path to directory containing executable jar
284     String jarPath = SimpleConfig.getProperty("WAREHOUSE_ExecutableJarPath");
285     if (jarPath == null) {
286       String errorMessage = "Property 'WAREHOUSE_ExecutableJarPath' not set" +
287       " in properties file 'AstroGridConfig.properties'";
288       log.error(errorMessage);
289       throw new DatabaseAccessException(errorMessage);
290     }
291     String sep = System.getProperty("file.separator");
292     if (sep == null) {
293       log.warn("Warning, couldn't get system file.separator, assuming " +
294           "WAREHOUSE_ExecutableJarPath is properly terminated " +
295           "with file separator");
296     }
297     else {
298       int pathlen = jarPath.length();
299       // If last char in path not separator, add separator
300       if (!(jarPath.substring(pathlen-1,pathlen).equalsIgnoreCase(sep))) {
301         jarPath = jarPath + sep;
302       }
303     }
304     // Extract name of executable jar
305     String jarName = SimpleConfig.getProperty("WAREHOUSE_ExecutableJarName");
306     if (jarName == null) {
307       String errorMessage =
308           "Property 'WAREHOUSE_ExecutableJarName' not set in " +
309           "properties file 'AstroGridConfig.properties'";
310       log.error(errorMessage);
311       throw new DatabaseAccessException(errorMessage);
312     }
313     return jarPath + jarName;
314   }
315 
316 
317   /***
318    * Adjust input string (SQL query) to escape XML special characters
319    * likely to cause problems.
320    * @return  Version of string with escaped XML special characters
321    */
322   protected String escapeXmlSpecialChars(String inString) {
323     String outString = "";
324     for (int i = 0; i < inString.length(); i++) {
325       char c = inString.charAt(i);
326       if (c == '<') {
327         outString = outString + "&lt;";
328       }
329       else if (c == '>') {
330         outString = outString + "&gt;";
331       }
332       else if (c == '&') {
333         outString = outString + "&amp;";
334       }
335       else {
336         outString = outString + c;
337       }
338     }
339     return outString;
340   }
341 
342    /*** Returns just the number of matches rather than the list of matches */
343    public long getCount(Account user, Query query, Querier querier) throws IOException {
344             throw new UnsupportedOperationException("Not done yet");
345    }
346   // ----------------------------------------------------------
347   // Fallback defaults for values that should be configured on a
348   // per-installation basis in the WarehouseServiceImpl.properties
349   private final String DEFAULT_WAREHOUSE_JVM = "/usr/bin/java";
350 
351   private final String TEMP_RESULTS_FILENAME = "warehouseResults.xml";
352 //================================================================
353 
354    /*** Returns the formats that this plugin can provide.  Asks the results class; override in subclasse if nec */
355    public String[] getFormats() {
356       return VotableInResults.getFormats();
357    }
358    
359    
360    
361 }
362 /*
363 $Log: WarehouseQuerier.java,v $
364 Revision 1.6  2004/11/22 14:43:21  jdt
365 Restored Martin's changes from PAL_MCHJDT_705
366 
367 Revision 1.4  2004/11/12 13:49:12  mch
368 Fix where keyword maker might not have had keywords made
369 
370 Revision 1.3  2004/11/03 00:17:56  mch
371 PAL_MCH Candidate 2 merge
372 
373 Revision 1.2.6.1  2004/10/27 00:43:39  mch
374 Started adding getCount, some resource fixes, some jsps
375 
376 Revision 1.2  2004/10/18 13:11:30  mch
377 Lumpy Merge
378 
379 Revision 1.1.6.1  2004/10/15 19:59:06  mch
380 Lots of changes during trip to CDS to improve int test pass rate
381 
382 Revision 1.1  2004/09/28 15:02:13  mch
383 Merged PAL and server packages
384 
385 Revision 1.14  2004/09/07 00:54:20  mch
386 Tidied up Querier/Plugin/Results, and removed deprecated SPI-visitor-SQL-translator
387 
388 Revision 1.13  2004/04/01 17:10:31  mch
389 Removed unused import
390 
391 Revision 1.12  2004/04/01 10:10:09  eca
392 Replaced PostgresAdqlQueryTranslator with ogsadai.AdqlQueryTranslator.
393 
394 
395 
396 01/04/04 ElizabethAuden
397 
398 Revision 1.11  2004/03/26 15:52:47  eca
399 datacenter.queriers.ogsadai.AdqlQueryTranslator now updated for
400 1) optimized for Postgres
401 2) conforms to ADQLSchemav06
402 
403 Also, imported new AdqlQueryTranslator in WarehouseQuerier
404 
405 25/03/04 Elizabeth Auden
406 
407 Revision 1.10  2004/03/24 15:57:31  kea
408 Updated Javadocs etc.
409 
410 Revision 1.9  2004/03/17 18:24:15  kea
411 Integrating new javaapp using del-to-file, del-to-gridftp.
412 
413 Revision 1.8  2004/03/17 15:48:33  kea
414 Tidying method sigs.
415 
416 Revision 1.7  2004/03/17 13:53:29  mch
417 MCH botch to compile
418 
419 Revision 1.6  2004/03/17 12:20:54  kea
420 Removing XSLT rowset->VOTable conversions, now done in OGSA-DAI.
421 Interim checkin, end-to-end not working yet.
422 
423 Revision 1.5  2004/03/15 17:11:14  mch
424 'botch' fix for duplicate Workspace id
425 
426 Revision 1.4  2004/03/12 20:04:57  mch
427 It05 Refactor (Client)
428 
429 Revision 1.3  2004/03/12 04:45:26  mch
430 It05 MCH Refactor
431 
432 Revision 1.2  2004/01/24 20:44:25  gtr
433 Merged from GDW-integration branch.
434 
435 Revision 1.1.2.3  2004/01/24 20:37:33  gtr
436 Merged from GDW-integration branch.
437 
438 Revision 1.1.2.2  2004/01/24 17:38:00  gtr
439 Datacenter client has changed the Query class back to _query.
440 
441 Revision 1.1.2.1  2004/01/22 14:56:32  gtr
442 Transfered from astrogrid/warehouse, with package name changed to be in
443 org.astrogrid.datacenter.*.
444 
445 Revision 1.18  2004/01/21 15:59:40  gtr
446 _query class is changed to Query.
447 
448 Revision 1.17  2004/01/19 12:47:54  kea
449 Giving proper WAREHOUSE_ prefix to toplevel warehouse config properties
450 in AstroGridConfig.properties file.
451 
452 Revision 1.16  2004/01/16 12:19:09  kea
453 Fixing broken Javadoc comments.
454 
455 Revision 1.15  2004/01/15 18:36:57  kea
456 Now looks for properties in toplevel AstroGridConfig.properties,
457 using SimpleConfig initialised by datacenter infrastructure.
458 
459 Revision 1.14  2004/01/08 21:41:34  kea
460 Horrid kludge to convert datacenter "t.DEC" to "t.decl" for warehouse tables.
461 
462 Revision 1.13  2004/01/08 20:00:42  kea
463 Added XML special char escaping to sql strings returned by datacenter
464 ADQL->SQL conversion (otherwise OGSA-DAI perform docs get broken).
465 
466 Revision 1.12  2003/12/15 15:45:35  kea
467 Correcting misleading error message.
468 
469 Revision 1.11  2003/12/12 14:42:03  gtr
470 Unused import statements were removed.
471 
472 Revision 1.10  2003/12/11 13:14:48  kea
473 Moved OGSA-DAI host and registry location into WarehouseQuerier properties
474 file so they can be edited more easily in a tomcat inst.
475 
476 Revision 1.9  2003/12/10 12:27:56  kea
477 Finding JVM to shell out to from JAVA_HOME.
478 
479 Revision 1.8  2003/12/09 12:19:34  kea
480 Changed shelling-out to use executable jar (including adjustments
481 to properties file).
482 
483 Revision 1.7  2003/12/08 20:16:54  kea
484 Added JavaDoc.  Changed properties to use Java-style capitalisation.
485 Misc. small tidyings.
486 
487 Revision 1.6  2003/12/04 18:54:56  kea
488 Minimal logging added - more to follow.
489 
490 Revision 1.5  2003/12/02 16:22:13  kea
491 Moved ogsa-dai functionality out into GdsQuerierDelegate.
492 Misc. changes to accommodate changes in datacenter codebase.
493 
494 Revision 1.2  2003/11/26 19:46:19  kea
495 Basic datacenter-style querier, now fully integrated so it should
496 when invoked via datacenter framework.
497 Needs a lot of cleaning up and documentation added.
498 May need revision to meet forthcoming datacenter codebase changes.
499 
500 Revision 1.1  2003/11/19 17:33:40  kea
501 Initial Querier functionality for integration with the database.
502 This compiles and sort-of runs at the command-line;  however, we
503 currently have no means of getting the OGSA-DAI results into the
504 format expected by the datacenter.  (They currently get dumped to
505 /tmp/WS_OGSA_OUTPUT).
506 */