View Javadoc

1   /*
2    * $Id: VoDescriptionServer.java,v 1.16 2004/11/22 14:43:21 jdt Exp $
3    *
4    * (C) Copyright Astrogrid...
5    */
6   
7   package org.astrogrid.datacenter.metadata;
8   import java.io.IOException;
9   import java.io.StringWriter;
10  import java.lang.reflect.Constructor;
11  import java.net.URL;
12  import java.text.SimpleDateFormat;
13  import java.util.Calendar;
14  import java.util.Date;
15  import java.util.GregorianCalendar;
16  import java.util.Locale;
17  import java.util.TimeZone;
18  import javax.xml.parsers.ParserConfigurationException;
19  import org.apache.commons.logging.Log;
20  import org.apache.commons.logging.LogFactory;
21  import org.astrogrid.config.PropertyNotFoundException;
22  import org.astrogrid.config.SimpleConfig;
23  import org.astrogrid.datacenter.DsaDomHelper;
24  import org.astrogrid.datacenter.cone.ConeResourceServer;
25  import org.astrogrid.datacenter.service.DataServer;
26  import org.astrogrid.datacenter.service.skynode.v074.SkyNodeResourceServer;
27  import org.astrogrid.io.xml.XmlPrinter;
28  import org.astrogrid.io.xml.XmlTagPrinter;
29  import org.astrogrid.registry.RegistryException;
30  import org.astrogrid.registry.client.RegistryDelegateFactory;
31  import org.astrogrid.registry.client.admin.RegistryAdminService;
32  import org.astrogrid.util.DomHelper;
33  import org.w3c.dom.Document;
34  import org.w3c.dom.Element;
35  import org.w3c.dom.NodeList;
36  import org.xml.sax.SAXException;
37  
38  /***
39   * Serves the service's VoDescrption.
40   * <p>
41   * This file includes a VODescrption element, but at the moment I am assuming that
42   * the VODescription might be wrapped in other by combining the various Resources returned
43   * by the configured MetataPlugins into a VODescription.
44   *
45   * <p>See package documentation.
46   * <p>
47   * @author M Hill
48   */
49  
50  public class VoDescriptionServer {
51     protected static Log log = LogFactory.getLog(VoDescriptionServer.class);
52     
53     private static Document cache = null;
54     
55     public static final String AUTHID_KEY = "datacenter.authorityId";
56     public static final String RESKEY_KEY = "datacenter.resourceKey";
57     
58     public final static String VODESCRIPTION_ELEMENT =
59                 "<VODescription  xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' "+
60                                 "xmlns:cea='http://www.ivoa.net/xml/CEAService/v0.1' "+
61                                 "xmlns:ceapd='http://www.astrogrid.org/schema/AGParameterDefinition/v1' "+
62                                 "xmlns:ceab='http://www.astrogrid.org/schema/CommonExecutionArchitectureBase/v1' "+
63                                 "xmlns:vr='http://www.ivoa.net/xml/VOResource/v0.9' "+
64                                 "xmlns='http://www.ivoa.net/xml/VOResource/v0.9' "+  //default namespace
65                      ">";
66     public final static String VODESCRIPTION_ELEMENT_END ="</VODescription>";
67  
68     /*** used to format dates so that the registry can process them. eg 2005-11-04T15:34:22Z -
69      * the date must be GMT */
70     public final static SimpleDateFormat REGISTRY_DATEFORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
71  
72     /***
73      * Returns the whole metadata file as a DOM document
74      */
75     public synchronized static Document getVoDescription() throws IOException {
76        if (cache == null) {
77           try {
78              cache = DomHelper.newDocument(makeVoDescription().toString());
79              
80              //check it's OK
81              validate(cache);
82           }
83           catch (ParserConfigurationException e) {
84              throw new RuntimeException("Server not setup properly: "+e,e);
85           }
86           catch (SAXException e) {
87              throw new MetadataException("XML error with Metadata: "+e,e);
88           }
89        }
90        return cache;
91     }
92     
93     /*** Checks that the given document is a valid vodescription, throwing an
94      * exception if not */
95     public static void validate(Document vod) throws MetadataException {
96        Element root = vod.getDocumentElement();
97  
98        NodeList children = root.getChildNodes();
99        
100       for (int i = 0; i < children.getLength(); i++) {
101          if (children.item(i) instanceof Element) {
102             Element resource = (Element) children.item(i);
103             
104             if (!resource.getNodeName().equals("Resource")) {
105                throw new MetadataException("VODescription Child "+i+" ("+resource.getNodeName()+") is not a Resource element");
106             }
107             
108             Element id = DsaDomHelper.getSingleChildByTagName(resource, "Identifier");
109             if (id == null) {
110                //no identifier - could add one but we don't know what resource key to give it
111                throw new MetadataException("Resource "+i+" (xsi:type="+resource.getAttribute("xsi:type")+") has no Identifier");
112             }
113             
114             DsaDomHelper.setElementValue(DsaDomHelper.ensuredGetSingleChild(id, "AuthorityID"), SimpleConfig.getSingleton().getString(VoDescriptionServer.AUTHID_KEY));
115             Element resKey = DsaDomHelper.getSingleChildByTagName(id, "ResourceKey");
116             if ((resKey == null) || (DomHelper.getValue(resKey).trim().length()==0)) {
117                //no resource key
118                throw new MetadataException("Identifier in Resource "+i+" (xsi:type="+resource.getAttribute("xsi:type")+") has no ResourceKey");
119             }
120          }
121       }
122       
123    }
124 
125    /*** Instantiates the class with the given name.  This is useful for things
126     * such as 'plugins', where a class name might be given in a configuration file.
127     * Rather messily throws Throwable because anything might have
128     * gone wrong in the constructor.
129     */
130    public static VoResourcePlugin createPlugin(String pluginClassName) {
131       
132       Object plugin = null;
133       
134       try {
135          log.debug("Creating VoResourcePlugin '"+pluginClassName+"'");
136          
137          Class qClass = Class.forName(pluginClassName);
138        
139          /* NWW - interesting bug here.
140           original code used class.newInstance(); this method doesn't declare it throws InvocationTargetException,
141           however, this exception _is_ thrown if an exception is thrown by the constructor (as is often the case at the moment)
142           worse, as InvocatioinTargetException is a checked exception, the compiler rejects code with a catch clause for
143           invocationTargetExcetpion - as it thinks it cannot be thrown.
144           this means the exception boils out of the code, and is unstoppable - dodgy
145           work-around - use the equivalent methods on java.lang.reflect.Constructor - which do throw the correct exceptions */
146          
147          Constructor constr = qClass.getConstructor(new Class[] { });
148          plugin = constr.newInstance(new Object[] { } );
149          
150       }
151       catch (ClassNotFoundException cnfe) {
152          throw new RuntimeException("Could not find metadata plugin class "+pluginClassName);
153       }
154       catch (NoSuchMethodException nsme) {
155          throw new RuntimeException("Bad metadata plugin specified ("+pluginClassName+") - has no zero-argument constructor");
156       }
157       catch (Throwable th) {
158          throw new RuntimeException("Bad metadata plugin specified ("+pluginClassName+")",th);
159       }
160       
161       if (!(plugin instanceof VoResourcePlugin)) {
162          throw new RuntimeException("Bad metadata plugin specified ("+pluginClassName+") - does not implement VoResourcePlugin");
163       }
164       
165       return (VoResourcePlugin) plugin;
166       
167    }
168    
169    /***
170     * Clears the cache - useful to call before doing a set of operations, forces
171     * metadata to be refreshed from disk
172     */
173    public synchronized static void clearCache() {
174       cache = null;
175    }
176    
177    /***
178     * Make a VODescription document out of all the voResourcePlugins, returning an
179     * unvalidated string.  This means we can view the made (finsihed) docuemnt
180     * separate from the validating process. */
181    public static String makeVoDescription() throws IOException, MetadataException {
182 
183       //get plugin list from config - need to add a default to the common method...
184       Object[] plugins  = null;
185       try {
186          plugins = SimpleConfig.getSingleton().getProperties(VoResourcePlugin.RESOURCE_PLUGIN_KEY);
187       } catch (PropertyNotFoundException pnfe)
188       {
189          //for backwards compatibility, look for old datacenter.metadata.plugin
190          String s = SimpleConfig.getSingleton().getString("datacenter.metadata.plugin",null);
191          if (s != null) {
192             plugins = new String[] { s };
193          }
194       }
195 
196       //start the vodescription document
197       StringBuffer vod = new StringBuffer();
198       vod.append(VODESCRIPTION_ELEMENT+"\n");
199       boolean ceaDone = false; //backwards compatiblity marker to see if CeaResource has been done
200       
201       //loop through plugins adding each one's list of resources
202       if (plugins != null) {
203          for (int p = 0; p < plugins.length; p++) {
204             //make plugin
205             VoResourcePlugin plugin = createPlugin(plugins[p].toString());
206             //get resources from plugin
207             addResources(vod, plugin);
208             
209             if (plugin instanceof CeaResourceServer) { ceaDone = true; }
210          }
211       }
212 
213       //add the standard ones - cea, cone etc
214       if (!ceaDone) { addResources(vod, new CeaResourceServer()); }
215       addResources(vod, new ConeResourceServer());
216       addResources(vod, new SkyNodeResourceServer());
217       
218       //finish vod element
219       vod.append(VODESCRIPTION_ELEMENT_END);
220 
221       return vod.toString();
222    }
223 
224    /*** Convenience routine for the above to load the resources from the given plugin
225     * to the given StringBuffer */
226    private static void addResources(StringBuffer b, VoResourcePlugin plugin) throws IOException {
227       String[] voResources = plugin.getVoResources();
228       //add to document
229       for (int r = 0; r < voResources.length; r++) {
230          b.append(voResources[r]);
231       }
232       return;
233    }
234 
235    /***
236     * Returns the Authority resource element of the description */
237    public static Element getAuthorityResource() throws IOException {
238       return getResource("AuthorityType");
239    }
240    
241    /***
242     * Returns the resource element of the given type eg 'AuthorityID'.
243     * Matches the given string against the attribute 'xsi:type' of the elements
244     * named 'Resource'
245     */
246    public static Element getResource(String type) throws IOException {
247       NodeList resources = getVoDescription().getElementsByTagName("Resource");
248       
249       for (int i = 0; i < resources.getLength(); i++) {
250          Element resource = (Element) resources.item(i);
251          if (resource.getAttribute("xsi:type").equals(type)) {
252             return resource;
253          }
254       }
255       return null; //not found
256    }
257 
258    /***
259     * Sends the voDescription to the registry, returning list of Registries that
260     * it was sent to
261     */
262    public static String[] pushToRegistry() throws IOException, RegistryException {
263       RegistryAdminService service = RegistryDelegateFactory.createAdmin();
264       service.update(getVoDescription());
265       return new String[] { SimpleConfig.getSingleton().getString(RegistryDelegateFactory.ADMIN_URL_PROPERTY) };
266    }
267 
268    /***
269     * Sends the voDescription to the given registry URL, returning list of Registries that
270     * it was sent to
271     */
272    public static void pushToRegistry(URL targetRegistry) throws IOException, RegistryException {
273       RegistryAdminService service = RegistryDelegateFactory.createAdmin(targetRegistry);
274       service.update(getVoDescription());
275    }
276    
277    /*** Adds an Identifier tag to the given XmlPrinter */
278    public static void writeIdentifier(XmlTagPrinter parent, String resourceKeyEnd) throws IOException {
279       XmlTagPrinter identifier = parent.newTag("Identifier");
280       identifier.writeTag("AuthorityID", SimpleConfig.getSingleton().getString(AUTHID_KEY));
281       identifier.writeTag("ResourceKey", SimpleConfig.getSingleton().getString(RESKEY_KEY)+resourceKeyEnd);
282    }
283 
284    /*** Writes out standard summary stuff to the given XmlPrinter */
285    public static void writeSummary(XmlTagPrinter parent) throws IOException {
286       parent.writeTag("Title", DataServer.getDatacenterName());
287       parent.writeTag("ShortName", SimpleConfig.getSingleton().getString("datacenter.shortname", ""));
288       
289       XmlTagPrinter summary = parent.newTag("Summary");
290       summary.writeTag("Description", SimpleConfig.getSingleton().getString("datacenter.description", ""));
291       summary.writeTag("ReferenceURL", SimpleConfig.getSingleton().getString("datacenter.url", ""));
292    }
293    
294    /***  Writes out standard curation stuff to the given XmlPrinter  */
295    public static void writeCuration(XmlTagPrinter parent) throws IOException {
296       XmlTagPrinter curation = parent.newTag("Curation");
297       String publisher = SimpleConfig.getSingleton().getString("datacenter.publisher",null);
298       if (publisher != null) {
299          XmlTagPrinter publisherTag = curation.newTag("Publisher");
300          publisherTag.writeTag("Title", publisher);
301       }
302 
303       XmlTagPrinter contact = curation.newTag("Contact");
304       contact.writeTag("Name", SimpleConfig.getSingleton().getString("datacenter.contact.name",""));
305       contact.writeTag("Email", SimpleConfig.getSingleton().getString("datacenter.contact.email",""));
306       contact.writeTag("Date", SimpleConfig.getSingleton().getString("datacenter.contact.date",""));
307    }
308    
309    public static void appendSummary(StringBuffer buffer) throws IOException {
310       StringWriter sw = new StringWriter();
311       XmlPrinter xp = new XmlPrinter(sw, false);
312       writeSummary(xp);
313       xp.close(); //make sure all tags are closed
314       buffer.append(sw.toString());
315    }
316 
317    public static void appendIdentifier(StringBuffer buffer, String resourceKeyEnd) throws IOException {
318       StringWriter sw = new StringWriter();
319       XmlPrinter xp = new XmlPrinter(sw, false);
320       writeIdentifier(xp, resourceKeyEnd);
321       xp.close(); //make sure all tags are closed
322       buffer.append(sw.toString());
323    }
324 
325    public static void appendCuration(StringBuffer buffer) throws IOException {
326       StringWriter sw = new StringWriter();
327       XmlPrinter xp = new XmlPrinter(sw, false);
328       writeCuration(xp);
329       xp.close(); //make sure all tags are closed
330       buffer.append(sw.toString());
331    }
332    
333    /*** Checks that the identifier elements are there and set to the local values,
334     * creating and setting if not.  The given resourceKeyEnd is appended to the
335     datacenter's resourceKey to give the appropriate full resource key */
336    public static void ensureIdentifier(Element resource, String resourceKeyEnd) {
337 
338       //check that each 'returns' resource has a child <Identifier>, as some early ones didn't and we want to catch them
339       Element voIdTag = DsaDomHelper.ensuredGetSingleChild(resource, "Identifier");
340       
341       String dsaResKey = SimpleConfig.getSingleton().getString(VoDescriptionServer.RESKEY_KEY);
342    
343       DsaDomHelper.setElementValue(DsaDomHelper.ensuredGetSingleChild(voIdTag, "AuthorityID"), SimpleConfig.getSingleton().getString(VoDescriptionServer.AUTHID_KEY));
344       DsaDomHelper.setElementValue(DsaDomHelper.ensuredGetSingleChild(voIdTag, "ResourceKey"), SimpleConfig.getSingleton().getString(VoDescriptionServer.RESKEY_KEY)+resourceKeyEnd);
345    }
346 
347    /*** Checks the summary elements are there and set to the local values (creating it if not) */
348    public static void ensureSummary(Element resource) {
349       DsaDomHelper.setElementValue(DsaDomHelper.ensuredGetSingleChild(resource, "Title"), DataServer.getDatacenterName());
350       DsaDomHelper.setElementValue(DsaDomHelper.ensuredGetSingleChild(resource, "ShortName"), SimpleConfig.getSingleton().getString("datacenter.shortname", ""));
351 
352       Element summary = DsaDomHelper.ensuredGetSingleChild(resource, "Summary");
353       DsaDomHelper.setElementValue(DsaDomHelper.ensuredGetSingleChild(summary, "Description"), SimpleConfig.getSingleton().getString("datacenter.description", ""));
354       DsaDomHelper.setElementValue(DsaDomHelper.ensuredGetSingleChild(summary, "ReferenceURL"), SimpleConfig.getSingleton().getString("datacenter.url", ""));
355    }
356 
357    /***  Checks the curation stuff is present and set to the local values (creating it if not) */
358    public static void ensureCuration(Element resource)  {
359       Element curation = DsaDomHelper.ensuredGetSingleChild(resource, "Curation");
360       String publisher = SimpleConfig.getSingleton().getString("datacenter.publisher",null);
361       if (publisher != null) {
362          Element title = DsaDomHelper.ensuredGetSingleChild(curation, "Title");
363          DsaDomHelper.setElementValue(title, publisher);
364       }
365 
366       Element contact = DsaDomHelper.ensuredGetSingleChild(curation, "Contact");
367       DsaDomHelper.setElementValue(DsaDomHelper.ensuredGetSingleChild(contact, "Name"), SimpleConfig.getSingleton().getString("datacenter.contact.name", ""));
368       DsaDomHelper.setElementValue(DsaDomHelper.ensuredGetSingleChild(contact, "Email"), SimpleConfig.getSingleton().getString("datacenter.contact.email", ""));
369       DsaDomHelper.setElementValue(DsaDomHelper.ensuredGetSingleChild(contact, "Date"), SimpleConfig.getSingleton().getString("datacenter.contact.date", ""));
370    }
371    
372    /*** Converts date to string suitable for registry */
373    public String toRegistryForm(Date givenDate) {
374       //deprecated methods
375       //long minsOffset = aDate.getTimezoneOffset();
376       //Date gmtDate = new Date( aDate.getTime() + aDate.getTimezoneOffset());
377       //this also leaves us with a date that has it's original timezone, but a new value.  It's really a different time...
378       
379       //cludge to get timezone of given date; write it out and then parse it...
380       SimpleDateFormat offsetGetter = new SimpleDateFormat("Z");
381       String offsetName = offsetGetter.format(givenDate);
382       TimeZone givenZone = TimeZone.getTimeZone(offsetName);
383       int offset = givenZone.getOffset(givenDate.getTime());
384 //      Date gmtDate = new Date(givenZone.getOffset();
385       
386       
387       //convert to GMT
388       Calendar localCalender = Calendar.getInstance();
389       TimeZone localZone = localCalender.getTimeZone();
390       Calendar ukcalender = new GregorianCalendar(Locale.ENGLISH);
391       TimeZone gmtZone = TimeZone.getTimeZone("GMT");
392 
393       return "";
394    }
395    
396 }
397 
398 
399 
400 
401 
402 
403 
404