1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 package uk.ac.rdg.resc.jstyx.gridservice.config;
30
31 import java.util.Vector;
32 import java.util.Iterator;
33 import java.io.StringReader;
34
35 import org.dom4j.Node;
36 import org.dom4j.io.SAXReader;
37 import org.dom4j.Document;
38 import org.dom4j.DocumentException;
39
40 import com.martiansoftware.jsap.JSAP;
41 import com.martiansoftware.jsap.JSAPException;
42 import com.martiansoftware.jsap.Parameter;
43 import com.martiansoftware.jsap.UnflaggedOption;
44
45 import uk.ac.rdg.resc.jstyx.StyxUtils;
46 import uk.ac.rdg.resc.jstyx.gridservice.server.*;
47
48 /***
49 * Class containing configuration info for a single Styx Grid Service
50 *
51 * @author Jon Blower
52 * $Revision: 569 $
53 * $Date: 2006-01-05 16:06:35 +0000 (Thu, 05 Jan 2006) $
54 * $Log$
55 * Revision 1.7 2006/01/05 16:06:35 jonblower
56 * SGS clients now deal with possibility that client could be created on a different server
57 *
58 * Revision 1.6 2006/01/05 12:09:15 jonblower
59 * Restructured configuration to give default values for server settings
60 *
61 * Revision 1.5 2005/12/01 08:29:47 jonblower
62 * Refactored XML config handling to simplify clients
63 *
64 * Revision 1.4 2005/11/10 19:50:43 jonblower
65 * Added code to handle output files
66 *
67 * Revision 1.3 2005/11/10 08:57:21 jonblower
68 * Added code to handle output files and streams
69 *
70 * Revision 1.2 2005/11/09 17:45:00 jonblower
71 * Changes to storing of XML config information
72 *
73 * Revision 1.1 2005/11/07 20:59:34 jonblower
74 * Refactored SGS config classes to new package
75 *
76 * Revision 1.20 2005/11/04 19:28:20 jonblower
77 * Changed structure of input files in config file and Styx namespace
78 *
79 * Revision 1.19 2005/11/03 07:42:47 jonblower
80 * Implemented JSAP-based parameter parsing
81 *
82 * Revision 1.14 2005/10/18 14:08:14 jonblower
83 * Removed inputfiles from namespace
84 *
85 * Revision 1.12 2005/08/02 08:05:18 jonblower
86 * Continuing to implement steering
87 *
88 * Revision 1.10 2005/08/01 16:38:05 jonblower
89 * Implemented simple parameter handling
90 *
91 * Revision 1.9 2005/06/14 07:45:16 jonblower
92 * Implemented setting of params and async notification of parameter changes
93 *
94 * Revision 1.8 2005/05/19 18:42:07 jonblower
95 * Implementing specification of input files required by SGS
96 *
97 * Revision 1.7 2005/05/16 11:00:53 jonblower
98 * Changed SGS config XML file structure: separated input and output streams and changed some tag names
99 *
100 * Revision 1.6 2005/05/13 16:49:34 jonblower
101 * Coded dynamic detection and display of service data, also included streams in config file
102 *
103 * Revision 1.5 2005/05/11 15:14:30 jonblower
104 * Implemented more flexible definition of service data elements
105 *
106 * Revision 1.4 2005/05/11 13:45:19 jonblower
107 * Converted SGS config code to use dom4j and Jaxen for XML parsing
108 *
109 * Revision 1.3 2005/04/27 16:11:35 jonblower
110 * Added capability to add documentation files to SGS namespace
111 *
112 * Revision 1.2 2005/03/26 14:27:53 jonblower
113 * Modified to use SGSConfigException
114 *
115 * Revision 1.1 2005/03/24 17:34:58 jonblower
116 * Initial import
117 *
118 */
119 public class SGSConfig
120 {
121 private SGSServerConfig serverConfig;
122 private Node rootNode;
123
124 private String name;
125 private String command;
126 private String workDir;
127 private String description;
128 private String configXMLForClient;
129
130 private Vector inputs;
131
132 private Vector outputs;
133 private Vector docFiles;
134 private Vector params;
135 private JSAP paramParser;
136 private Vector steerables;
137 private Vector serviceData;
138
139 /***
140 * This is called by the SGS server program to generate a configuration
141 * object from the XML config file.
142 * @param gridService The Node in the XML config file that is at the
143 * root of this Styx Grid Service
144 * @param serverConfig Configuration of the server
145 * @throws IllegalArgumentException if the name of the SGS contains
146 * a space.
147 */
148 public SGSConfig(Node gridService, SGSServerConfig serverConfig)
149 throws SGSConfigException
150 {
151 this.init(gridService);
152 this.serverConfig = serverConfig;
153 this.rootNode = gridService;
154 this.workDir = this.serverConfig.getCacheLocation() +
155 StyxUtils.SYSTEM_FILE_SEPARATOR + name;
156 this.setConfigXMLForClient();
157
158
159 this.docFiles = new Vector();
160 Iterator docListIter = gridService.selectNodes("docs/doc").iterator();
161 while(docListIter.hasNext())
162 {
163 Node docEl = (Node)docListIter.next();
164 String name = docEl.valueOf("@name");
165 String location = docEl.valueOf("@location");
166 this.docFiles.add(new DocFile(name, location));
167 }
168 }
169
170 /***
171 * This constructor is called by client programs to create an SGSConfig
172 * object from an XML snippet that has been read over the Styx interface.
173 * This does not populate any of the server-specific fields of this class
174 * (e.g. the working directory). After calling this constructor, clients
175 * will have access to all the fields that relate to <b>instances</b> of
176 * this SGS.
177 * @param configXML XML snippet representing the Styx Grid Service
178 */
179 public SGSConfig(String configXML) throws SGSConfigException
180 {
181
182
183
184 SAXReader reader = new SAXReader(false);
185 try
186 {
187 Document doc = reader.read(new StringReader(configXML));
188 this.init(doc.getRootElement());
189 }
190 catch(DocumentException de)
191 {
192
193 throw new SGSConfigException("Error parsing config XML: " + de.getMessage());
194 }
195 }
196
197 /***
198 * Initializes class variables from the given Node in an XML document
199 */
200 private void init(Node gridService) throws SGSConfigException
201 {
202 this.name = gridService.valueOf("@name");
203
204 if (this.name.indexOf(" ") != -1)
205 {
206
207 throw new IllegalArgumentException("The name of an SGS cannot contain a space");
208 }
209 this.command = gridService.valueOf("@command");
210 this.description = gridService.valueOf("@description");
211 boolean usingStdin = false;
212
213
214 this.params = new Vector();
215 this.paramParser = new JSAP();
216 Iterator paramListIter = gridService.selectNodes("params/param").iterator();
217 while(paramListIter.hasNext())
218 {
219 Node paramEl = (Node)paramListIter.next();
220 SGSParam param = new SGSParam(paramEl);
221 this.params.add(param);
222 try
223 {
224 this.paramParser.registerParameter(param.getParameter());
225 }
226 catch (JSAPException jsape)
227 {
228 throw new SGSConfigException("Error parsing parameters: " + jsape.getMessage());
229 }
230 }
231
232
233 this.inputs = new Vector();
234 Iterator inputListIter = gridService.selectNodes("inputs/input").iterator();
235 while(inputListIter.hasNext())
236 {
237 Node input = (Node)inputListIter.next();
238 SGSInput sgsIn = new SGSInput(input.valueOf("@type"), input.valueOf("@name"));
239 if (sgsIn.getType() == SGSInput.STREAM)
240 {
241 usingStdin = true;
242 }
243 else if (sgsIn.getType() == SGSInput.FILE_FROM_PARAM)
244 {
245
246
247 boolean found = false;
248 for (int i = 0; i < this.params.size() && !found; i++)
249 {
250 SGSParam param = (SGSParam)this.params.get(i);
251 if (param.getName().equals(sgsIn.getName()))
252 {
253 found = true;
254 param.setInputFile(sgsIn);
255 }
256 }
257 if (!found)
258 {
259 throw new SGSConfigException("Error setting input files:" +
260 " parameter " + sgsIn.getName() + " does not exist");
261 }
262 }
263 this.inputs.add(sgsIn);
264 }
265
266
267 this.outputs = new Vector();
268 Iterator outputListIter = gridService.selectNodes("outputs/output").iterator();
269 while(outputListIter.hasNext())
270 {
271 Node output = (Node)outputListIter.next();
272 SGSOutput sgsOut = new SGSOutput(output.valueOf("@type"), output.valueOf("@name"));
273 if (sgsOut.getType() == SGSOutput.FILE_FROM_PARAM)
274 {
275
276
277 boolean found = false;
278 for (int i = 0; i < this.params.size() && !found; i++)
279 {
280 SGSParam param = (SGSParam)this.params.get(i);
281 if (param.getName().equals(sgsOut.getName()))
282 {
283 found = true;
284
285
286 if (param.getParameter() instanceof UnflaggedOption)
287 {
288 UnflaggedOption uo = (UnflaggedOption)param.getParameter();
289 if (uo.isGreedy())
290 {
291 throw new SGSConfigException("Cannot link an" +
292 " output file to a greedy parameter");
293 }
294 }
295 param.setOutputFile(sgsOut);
296 }
297 }
298 if (!found)
299 {
300 throw new SGSConfigException("Error setting output files:" +
301 " parameter " + sgsOut.getName() + " does not exist");
302 }
303 }
304 this.outputs.add(sgsOut);
305 }
306
307
308 this.steerables = new Vector();
309 Iterator steerableListIter = gridService.selectNodes("steering/steerable").iterator();
310 while(steerableListIter.hasNext())
311 {
312 Node steerableEl = (Node)steerableListIter.next();
313 this.steerables.add(new Steerable(steerableEl));
314 }
315
316
317 this.serviceData = new Vector();
318 Iterator serviceDataIter =
319 gridService.selectNodes("serviceData/serviceDataElement").iterator();
320 while(serviceDataIter.hasNext())
321 {
322 Node sdEl = (Node)serviceDataIter.next();
323 SDEConfig sdeConf = new SDEConfig(sdEl);
324
325
326 if (sdeConf.getName().equals("bytesConsumed") && !usingStdin)
327 {
328 throw new SGSConfigException("The bytesConsumed service data" +
329 " element is only available when stdin is also available.");
330 }
331 this.serviceData.add(sdeConf);
332 }
333 }
334
335 /***
336 * @return the configuration (port, SSL details etc) of the server
337 */
338 public SGSServerConfig getServerConfig()
339 {
340 return this.serverConfig;
341 }
342
343 /***
344 * @return the name of the service
345 */
346 public String getName()
347 {
348 return this.name;
349 }
350
351 /***
352 * @return the command string that is run when the SGS is started. This
353 * is the string that is passed to Runtime.exec(). This
354 * is only meaningful for server-side code (if a client calls this method it
355 * will return null).
356 */
357 public String getCommand()
358 {
359 return this.command;
360 }
361
362 /***
363 * @return a String that briefly describes this SGS.
364 */
365 public String getDescription()
366 {
367 return this.description;
368 }
369
370 /***
371 * @return the working directory of this SGS. Each instance of the SGS
372 * will use a subdirectory of this directory as its working directory. This
373 * is only meaningful for server-side code (if a client calls this method it
374 * will return null).
375 */
376 public String getWorkingDirectory()
377 {
378 return this.workDir;
379 }
380
381 /***
382 * Sets the XML that was used to create this config object in a form
383 * suitable for clients to read. This is the same as the XML that was used
384 * to create the config object except that the command attribute of the root
385 * element and the documentation section are missing.
386 */
387 private void setConfigXMLForClient()
388 {
389
390 StringBuffer buf = new StringBuffer("<gridservice name=\"");
391 buf.append(this.name);
392 buf.append("\" description=\"");
393 buf.append(this.description);
394 buf.append("\">");
395
396
397
398 Iterator it = this.rootNode.selectNodes("*").iterator();
399 while (it.hasNext())
400 {
401 Node node = (Node)it.next();
402 if (!node.getName().equals("docs"))
403 {
404 buf.append(node.asXML());
405 }
406 }
407
408 buf.append("</gridservice>");
409 this.configXMLForClient = buf.toString();
410 }
411
412 /***
413 * @return the XML that was used to create this config object in a form
414 * suitable for clients to read. This is the same as the XML that was used
415 * to create the config object except that the command attribute of the root
416 * element and the documentation section are missing.
417 */
418 public String getConfigXMLForClient()
419 {
420 return this.configXMLForClient;
421 }
422
423 /***
424 * @return Vector of SGSInput objects containing details of all the
425 * input files and streams expected by the service
426 */
427 public Vector getInputs()
428 {
429 return this.inputs;
430 }
431
432 /***
433 * @return Vector of SGSOutput objects containing details of all the
434 * output files and streams exposed by the service
435 */
436 public Vector getOutputs()
437 {
438 return this.outputs;
439 }
440
441 /***
442 * @return JSAP object that is used to parse the command-line parameters
443 */
444 public JSAP getParamParser()
445 {
446 return this.paramParser;
447 }
448
449 /***
450 * @return Vector containing details of all the parameters (as SGSParam objects)
451 */
452 public Vector getParams()
453 {
454 return this.params;
455 }
456
457 /***
458 * @return Vector of Steerable objects containing details of the parameters
459 * that can be adjusted as the executable is running
460 */
461 public Vector getSteerables()
462 {
463 return this.steerables;
464 }
465
466 /***
467 * @return Vector of documentation file objects
468 */
469 public Vector getDocFiles()
470 {
471 return this.docFiles;
472 }
473
474 /***
475 * @return Vector of service data objects
476 */
477 public Vector getServiceData()
478 {
479 return this.serviceData;
480 }
481 }