1
2
3
4
5
6
7
8
9
10
11 package org.astrogrid.applications.manager;
12
13 import org.astrogrid.applications.CeaException;
14 import org.astrogrid.applications.beans.v1.ApplicationBase;
15 import org.astrogrid.applications.beans.v1.ApplicationList;
16 import org.astrogrid.applications.contracts.Configuration;
17 import org.astrogrid.applications.description.ApplicationDescription;
18 import org.astrogrid.applications.description.ApplicationDescriptionLibrary;
19 import org.astrogrid.applications.description.DescriptionUtils;
20 import org.astrogrid.applications.description.exception.ApplicationDescriptionNotFoundException;
21 import org.astrogrid.applications.description.registry.IvornUtil;
22 import org.astrogrid.common.bean.v1.Namespaces;
23 import org.astrogrid.component.descriptor.ComponentDescriptor;
24 import org.astrogrid.registry.beans.v10.cea.ApplicationDefinition;
25 import org.astrogrid.registry.beans.v10.cea.CeaApplicationType;
26 import org.astrogrid.registry.beans.v10.cea.CeaServiceType;
27 import org.astrogrid.registry.beans.v10.cea.ManagedApplications;
28 import org.astrogrid.registry.beans.v10.cea.Parameters;
29 import org.astrogrid.registry.beans.v10.resource.AccessURL;
30 import org.astrogrid.registry.beans.v10.wsinterface.VOResources;
31 import org.astrogrid.test.AstrogridAssert;
32 import org.astrogrid.test.schema.SchemaMap;
33
34 import org.apache.axis.utils.XMLUtils;
35 import org.apache.commons.logging.Log;
36 import org.apache.commons.logging.LogFactory;
37 import org.exolab.castor.xml.CastorException;
38 import org.exolab.castor.xml.MarshalException;
39 import org.exolab.castor.xml.Marshaller;
40 import org.exolab.castor.xml.Unmarshaller;
41 import org.exolab.castor.xml.ValidationException;
42 import org.w3c.dom.Document;
43 import org.xml.sax.InputSource;
44
45 import java.io.IOException;
46 import java.io.InputStream;
47 import java.io.StringReader;
48 import java.io.StringWriter;
49 import java.net.URL;
50
51 import javax.xml.parsers.DocumentBuilder;
52 import javax.xml.parsers.DocumentBuilderFactory;
53 import javax.xml.parsers.FactoryConfigurationError;
54 import javax.xml.parsers.ParserConfigurationException;
55 import javax.xml.transform.Result;
56 import javax.xml.transform.Source;
57 import javax.xml.transform.Templates;
58 import javax.xml.transform.Transformer;
59 import javax.xml.transform.TransformerConfigurationException;
60 import javax.xml.transform.TransformerException;
61 import javax.xml.transform.TransformerFactory;
62 import javax.xml.transform.TransformerFactoryConfigurationError;
63 import javax.xml.transform.dom.DOMResult;
64 import javax.xml.transform.dom.DOMSource;
65 import javax.xml.transform.stream.StreamResult;
66 import javax.xml.transform.stream.StreamSource;
67
68 import junit.framework.Test;
69 import junit.framework.TestCase;
70
71 /***
72 * Standard implementation of the
73 * {@link org.astrogrid.applications.manager.MetadataService}component.
74 *
75 * @author Noel Winstanley nw@jb.man.ac.uk 21-May-2004
76 * @author pharriso@eso.org 02-Jun-2005
77 * @TODO - might want some more thought wrt overriding in other cards.
78 */
79 public class DefaultMetadataService implements MetadataService,
80 ComponentDescriptor {
81 private static final Log logger = LogFactory
82 .getLog(DefaultMetadataService.class);
83
84 private static final String FORMATTER_XSL = "registryFormatter.xsl";
85
86 /*** configuration settings */
87 private Configuration configuration;
88
89 /*** library to generate description for */
90 private final ApplicationDescriptionLibrary lib;
91
92
93 /***
94 * Construct a new DefaultMetadataService
95 *
96 * @param lib
97 * The library of descriptions for which to build a registry entry.
98 * @param urls
99 * URLs needed for configuration.
100 */
101 public DefaultMetadataService(ApplicationDescriptionLibrary lib,
102 Configuration configuration) {
103 this.lib = lib;
104 this.configuration = configuration;
105 }
106
107 /***
108 * Create the registry entry....
109 *
110 * @return a VOResources.
111 */
112 public VOResources makeEntry()
113 throws ApplicationDescriptionNotFoundException,
114 MarshalException,
115 ValidationException,
116 ParserConfigurationException,
117 FactoryConfigurationError,
118 TransformerException,
119 IOException {
120
121
122
123 VOResources template = this.makeTemplate();
124
125 VOResources vodesc = new VOResources();
126 CeaApplicationType applicationTemplate = (CeaApplicationType) template
127 .getResource(0);
128 CeaServiceType serviceTemplate = (CeaServiceType) template.getResource(1);
129
130 CeaServiceType service = cloneTemplate(serviceTemplate);
131 ManagedApplications managedApplications = new ManagedApplications();
132 service.setManagedApplications(managedApplications);
133 ApplicationList applist = makeApplist(lib);
134
135 for (int i = 0; i < applist.getApplicationDefnCount(); i++) {
136
137 ApplicationBase theapp = applist.getApplicationDefn(i);
138 ApplicationDescription theAppDesc = lib.getDescription(theapp
139 .getName());
140
141 if (theapp.getName() != null) {
142
143
144
145 CeaApplicationType appentry = makeApplicationEntry(
146 applicationTemplate, theapp);
147
148 appentry.getContent()
149 .setDescription(theAppDesc.getAppDescription());
150 appentry.getContent().setReferenceURL(theAppDesc.getReferenceURL());
151 appentry.setTitle(theAppDesc.getUIName());
152
153 String shortname = theAppDesc.getUIName();
154 if(shortname.length() > 16)
155 {
156 logger.warn("truncating "+shortname+"to 16 characters to fit in VO shortname");
157 shortname = shortname.substring(0, 15);
158 }
159 appentry.setShortName(shortname);
160 vodesc.addResource(appentry);
161
162 managedApplications.addApplicationReference(appentry
163 .getIdentifier());
164 }
165
166 }
167
168 AccessURL accessurl = new AccessURL();
169 accessurl.setContent(this.configuration.getServiceEndpoint().toString());
170 service.get_interface(0).setAccessURL(accessurl);
171 vodesc.addResource(service);
172 return vodesc;
173
174 }
175
176 /***
177 * Create and populate a new application entry.
178 *
179 * @param template
180 * The template on which the general application information in the
181 * entry is based.
182 * @param app
183 * Specific application information to be added to the entry.
184 * @return
185 * @throws MarshalException
186 * @throws ValidationException
187 */
188 private CeaApplicationType makeApplicationEntry(CeaApplicationType template,
189 ApplicationBase app) throws MarshalException, ValidationException {
190
191 CeaApplicationType entry = cloneTemplate(template);
192
193 entry.setIdentifier(makeIvorn(IvornUtil.extractAuthorityFragment(app
194 .getName()), IvornUtil.extractIDFragment(app.getName())));
195
196 ApplicationDefinition applicationDefinition = new ApplicationDefinition();
197
198 applicationDefinition.setInterfaces(app.getInterfaces());
199
200
201
202 Parameters regpar = new Parameters();
203 regpar.setParameterDefinition(app.getParameters().getParameter());
204 applicationDefinition.setParameters(regpar);
205
206 entry.setApplicationDefinition(applicationDefinition);
207 return entry;
208 }
209
210 /***
211 * Create a clone of the given object. Does this by using the castor
212 * marshalling/unmarshalling on the object.
213 *
214 * @param app
215 * @return
216 * @throws MarshalException
217 * @throws ValidationException
218 */
219 private CeaApplicationType cloneTemplate(CeaApplicationType app)
220 throws MarshalException, ValidationException {
221 StringWriter sw = new StringWriter();
222 CeaApplicationType newapp = null;
223 app.marshal(sw);
224 StringReader sr = new StringReader(sw.toString());
225 InputSource is = new InputSource(sr);
226 Unmarshaller um = new Unmarshaller(CeaApplicationType.class);
227 newapp = (CeaApplicationType) um.unmarshal(is);
228
229 return newapp;
230
231 }
232
233 /***
234 * Create a clone of the given object. Does this by using the castor
235 * marshalling/unmarshalling on the object.
236 *
237 * @param serv
238 * @return
239 * @throws MarshalException
240 * @throws ValidationException
241 */
242 private CeaServiceType cloneTemplate(CeaServiceType serv)
243 throws MarshalException, ValidationException {
244 CeaServiceType newserv = null;
245 StringWriter sw = new StringWriter();
246 serv.marshal(sw);
247
248 String sb = sw
249 .toString()
250 .replaceAll(
251 "xsi:type=\"WebService\"",
252 "xsi:type=\"java:org.astrogrid.registry.beans.v10.resource.dataservice.WebService\"");
253
254 StringReader sr = new StringReader(sb);
255 InputSource is = new InputSource(sr);
256 Unmarshaller um = new Unmarshaller(CeaServiceType.class);
257 newserv = (CeaServiceType) um.unmarshal(is);
258
259 return newserv;
260 }
261
262 /***
263 * Make an ApplicationList from a full configuration. This is a deep copy -
264 * new instances of the ApplicationBase objecst are created.
265 *
266 * @param clecConfig
267 * a configuration for a command line execution controller
268 * @return
269 * @TODO might be better to refactor the original schema so that there was a
270 * base type for the common execution contoller configs...
271 */
272 private ApplicationList makeApplist(ApplicationDescriptionLibrary lib)
273 throws ApplicationDescriptionNotFoundException {
274 ApplicationList result = new ApplicationList();
275 String names[] = lib.getApplicationNames();
276 for (int i = 0; i < names.length; i++) {
277 ApplicationDescription descr = lib.getDescription(names[i]);
278 ApplicationBase base = DescriptionUtils
279 .applicationDescription2ApplicationBase(descr);
280 result.addApplicationDefn(base);
281 }
282 return result;
283 }
284
285 /***
286 * Gets a URL leading to the current registration-template. The
287 * location of the template is set during construction.
288 */
289 public URL getRegistrationTemplate() {
290 return this.configuration.getRegistryTemplate();
291 }
292
293 /***
294 * This should potentially be overriden by subclasses.
295 * @see org.astrogrid.applications.component.ProvidesVODescription#getDescription()
296 * @todo could cache the result.
297 */
298 public VOResources getVODescription() throws Exception {
299 return makeEntry();
300 }
301
302 /***
303 * loads template fron url, builds objects from it. Has to "hack" the
304 * template to change it from being valid to allow castor to actually read
305 * it.
306 *
307 * @throws IOException
308 * @throws TransformerException
309 * @throws FactoryConfigurationError
310 * @throws ParserConfigurationException
311 */
312 private VOResources makeTemplate()
313 throws MarshalException,
314 ValidationException,
315 ParserConfigurationException,
316 FactoryConfigurationError,
317 TransformerException,
318 IOException {
319 logger.info("Registry template is read from " +
320 this.configuration.getRegistryTemplate());
321 String hackedtemplate
322 = transformTemplateForCastor(this.configuration.getRegistryTemplate().openStream(),
323 this.getClass().getResourceAsStream("/CastorHacker.xsl"));
324
325 Unmarshaller um = new Unmarshaller(VOResources.class);
326 um.setIgnoreExtraAttributes(true);
327 um.setIgnoreExtraElements(true);
328 StringReader sr = new StringReader(hackedtemplate);
329 InputSource is = new InputSource(sr);
330 VOResources temp = (VOResources) um.unmarshal(is);
331 return temp;
332 }
333
334 private String makeIvorn(String auth, String id) {
335 StringBuffer sb = new StringBuffer("ivo://");
336 sb.append(auth);
337 sb.append("/");
338 sb.append(id);
339 return sb.toString();
340 }
341
342 /***
343 * takes an xml valid template and "hacks" to form that castor can read. This
344 * involves setting xsi:type=java:class for each derived class of Resource,
345 * and is achieved via an xsl translation in an external file.
346 *
347 * @param in
348 * @param inxsl
349 * @return
350 * @throws ParserConfigurationException
351 * @throws FactoryConfigurationError
352 * @throws TransformerException
353 */
354 private String transformTemplateForCastor(InputStream in, InputStream inxsl)
355 throws ParserConfigurationException, FactoryConfigurationError,
356 TransformerException {
357
358 TransformerFactory factory = TransformerFactory.newInstance();
359
360
361 Templates template = factory.newTemplates(new StreamSource(inxsl));
362
363
364 Transformer xformer = template.newTransformer();
365
366
367 Source source = new StreamSource(in);
368
369
370 StringWriter sw = new StringWriter();
371
372
373 Result result = new StreamResult(sw);
374
375
376 xformer.transform(source, result);
377 return sw.toString();
378 }
379
380
381 /***
382 * This is final because the intention is that all differences between implementations are expressed by overriding @link DefaultMetadataService#getVODescription()
383 * @see org.astrogrid.applications.manager.MetadataService#returnRegistryEntry()
384 */
385 public final Document returnRegistryEntry() throws CeaException {
386
387 InputStream formatterXSL = null;
388 Document finalDoc = null;
389
390 try {
391
392
393
394
395
396
397 DocumentBuilder builder = DocumentBuilderFactory.newInstance()
398 .newDocumentBuilder();
399 StringWriter sw = new StringWriter(1000);
400 Marshaller marshaller = new Marshaller(sw);
401 marshaller.setDebug(true);
402 marshaller.setMarshalExtendedType(true);
403 marshaller.setSuppressXSIType(false);
404 marshaller.setMarshalAsDocument(true);
405
406
407
408
409 marshaller.setNamespaceMapping("cea", Namespaces.VOCEA);
410 marshaller.setNamespaceMapping("vr", Namespaces.VORESOURCE);
411 marshaller.setNamespaceMapping("ceapd", Namespaces.CEAPD);
412 marshaller.setNamespaceMapping("ceab", Namespaces.CEAB);
413 marshaller.setNamespaceMapping("vs", Namespaces.VODATASERVICE);
414 marshaller.marshal(this.getVODescription());
415
416
417
418 TransformerFactory fac = TransformerFactory.newInstance();
419 String xslpath = DefaultMetadataService.class.getPackage() + FORMATTER_XSL;
420 formatterXSL = DefaultMetadataService.class.getResourceAsStream(FORMATTER_XSL);
421 Source formatter = new StreamSource(formatterXSL);
422 Templates xsltTemplate = fac.newTemplates(formatter);
423 Transformer xformer = xsltTemplate.newTransformer();
424 StringReader sr = new StringReader(sw.toString());
425 Source source = new StreamSource(sr);
426 finalDoc = builder.newDocument();
427 Result result = new DOMResult(finalDoc);
428 xformer.transform(source, result);
429 } catch (Exception e) {
430 logger.error("could not marshal VODescription", e);
431 throw new CeaException("could not marshal VODescription", e);
432 }
433
434 return finalDoc;
435 }
436
437 /***
438 * @see org.astrogrid.component.descriptor.ComponentDescriptor#getName()
439 */
440 public String getName() {
441 return "Standard CEA Server Description";
442 }
443
444 /***
445 * @see org.astrogrid.component.descriptor.ComponentDescriptor#getDescription()
446 */
447 public String getDescription() {
448 StringBuffer sb = new StringBuffer("Standard implementation of the service description component`n");
449 try {
450 Document doc = this.returnRegistryEntry();
451 StringWriter sw = new StringWriter();
452 XMLUtils.PrettyDocumentToWriter(doc, sw);
453 sb.append("VODescription \n" + XMLUtils.xmlEncodeString(sw.toString()));
454 } catch (Exception e) {
455 sb.append( "Could not display description: " + e.getMessage());
456 }
457
458 return sb.toString();
459 }
460
461 /***
462 * @see org.astrogrid.component.descriptor.ComponentDescriptor#getInstallationTest()
463 */
464 public Test getInstallationTest() {
465 return new InstallationTest("testGetRegistryEntry");
466 }
467
468 public class InstallationTest extends TestCase {
469
470 public InstallationTest(String arg0) {
471 super(arg0);
472 }
473
474 public void testGetRegistryEntry() throws Exception {
475 Document entry = returnRegistryEntry();
476 assertNotNull(entry);
477 AstrogridAssert.assertSchemaValid(entry, "VOResources", SchemaMap.ALL);
478 }
479
480 }
481 }
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555