View Javadoc

1   /*
2    * $Id: VoDescriptionServer.java,v 1.9 2005/03/23 17:24:48 mch Exp $
3    *
4    * (C) Copyright Astrogrid...
5    */
6   
7   package org.astrogrid.dataservice.metadata;
8   import java.io.IOException;
9   import java.lang.reflect.Constructor;
10  import java.net.URL;
11  import org.apache.commons.logging.Log;
12  import org.apache.commons.logging.LogFactory;
13  import org.astrogrid.cfg.PropertyNotFoundException;
14  import org.astrogrid.cfg.ConfigFactory;
15  import org.astrogrid.dataservice.metadata.queryable.QueryableResourceReader;
16  import org.astrogrid.dataservice.metadata.v0_10.VoResourceSupport;
17  import org.astrogrid.dataservice.service.cea.CeaResources;
18  import org.astrogrid.dataservice.service.cone.ConeResources;
19  import org.astrogrid.registry.RegistryException;
20  import org.astrogrid.registry.client.RegistryDelegateFactory;
21  import org.astrogrid.registry.client.admin.RegistryAdminService;
22  import org.astrogrid.slinger.vospace.IVORN;
23  import org.astrogrid.tableserver.test.SampleStarsPlugin;
24  import org.astrogrid.xml.DomHelper;
25  import org.w3c.dom.Document;
26  import org.w3c.dom.Element;
27  import org.w3c.dom.NodeList;
28  import org.xml.sax.SAXException;
29  import java.net.URISyntaxException;
30  
31  /***
32   * Assembles the various VoResource elements provided by the plugins, and
33   * serves them all up wrapped in a VoDescrption element for submitting to registries
34   * @see VoResourceSupport for how resource elements are generated
35   * @see package documentation
36   * <p>
37   * @author M Hill
38   */
39  
40  public class VoDescriptionServer {
41     protected static Log log = LogFactory.getLog(VoDescriptionServer.class);
42     
43     private static Document cache = null;
44     
45     public static final String QUERYABLE_PLUGIN = "datacenter.queryable.plugin";
46     public final static String RESOURCE_PLUGIN_KEY = "datacenter.resource.plugin";
47     
48     public final static String VODESCRIPTION_ELEMENT =
49                 "<VOResources  xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' "+
50                                "xmlns:vor='http://www.ivoa.net/xml/VOResource/v0.10' "+
51                                "xmlns='http://www.ivoa.net/xml/VOResource/v0.10' "+  //default namespace
52                      ">";
53     public final static String VODESCRIPTION_ELEMENT_END ="</VOResources>";
54  
55  
56     /***
57      * Returns the whole metadata file as a DOM document
58      */
59     public synchronized static Document getVoDescription() throws IOException {
60        if (cache == null) {
61           try {
62              cache = DomHelper.newDocument(makeVoDescription());
63              
64           }
65           catch (SAXException e) {
66              throw new MetadataException("XML error with Metadata: "+e,e);
67           }
68        }
69        return cache;
70     }
71     
72     /*** Checks that the given document is a valid vodescription, throwing an
73      * exception if not */
74     public static void validateDescription(String vod) throws SAXException, MetadataException {
75        Element root = null;
76        try {
77           root = DomHelper.newDocument(vod).getDocumentElement();
78        }
79        catch (IOException e) {
80           throw new RuntimeException(e);
81        }
82        
83        NodeList children = root.getChildNodes();
84        
85        for (int i = 0; i < children.getLength(); i++) {
86           if (children.item(i) instanceof Element) {
87              Element resource = (Element) children.item(i);
88              
89              if (!resource.getLocalName().equals("Resource")) {
90                 throw new MetadataException("VODescription Child "+i+" ("+resource.getNodeName()+") is not a Resource element");
91              }
92              
93              Element idNode = DomHelper.getSingleChildByTagName(resource, "identifier");
94              if (idNode == null) {
95                 //no identifier - could add one but we don't know what resource key to give it
96                 throw new MetadataException("Resource "+i+" (xsi:type="+resource.getAttribute("xsi:type")+") has no <identifier>");
97              }
98              
99              String configAuth = ConfigFactory.getCommonConfig().getString(VoResourceSupport.AUTHID_KEY);
100             IVORN id = null;
101             try {
102                id = new IVORN(DomHelper.getValueOf(idNode));
103             }
104             catch (URISyntaxException e) {
105                throw new MetadataException("<identifier> '"+id+"' is not a valid IVORN ");
106             }
107             
108             if (!id.getAuthority().startsWith(configAuth)) {
109                throw new MetadataException("<identifier> '"+id+"' does not start with configured authority "+configAuth);
110             }
111          }
112       }
113       
114    }
115 
116    /*** Instantiates the class with the given name.  This is useful for things
117     * such as 'plugins', where a class name might be given in a configuration file.
118     * Rather messily throws Throwable because anything might have
119     * gone wrong in the constructor.
120     */
121    public static VoResourcePlugin createVoResourcePlugin(String pluginClassName) {
122       
123       Object plugin = null;
124       
125       try {
126          log.debug("Creating VoResourcePlugin '"+pluginClassName+"'");
127          
128          Class qClass = Class.forName(pluginClassName);
129        
130          /* NWW - interesting bug here.
131           original code used class.newInstance(); this method doesn't declare it throws InvocationTargetException,
132           however, this exception _is_ thrown if an exception is thrown by the constructor (as is often the case at the moment)
133           worse, as InvocatioinTargetException is a checked exception, the compiler rejects code with a catch clause for
134           invocationTargetExcetpion - as it thinks it cannot be thrown.
135           this means the exception boils out of the code, and is unstoppable - dodgy
136           work-around - use the equivalent methods on java.lang.reflect.Constructor - which do throw the correct exceptions */
137          
138          Constructor constr = qClass.getConstructor(new Class[] { });
139          plugin = constr.newInstance(new Object[] { } );
140          
141       }
142       catch (ClassNotFoundException cnfe) {
143          throw new RuntimeException("Could not find metadata plugin class "+pluginClassName);
144       }
145       catch (NoSuchMethodException nsme) {
146          throw new RuntimeException("Bad metadata plugin specified ("+pluginClassName+") - has no zero-argument constructor");
147       }
148       catch (Throwable th) {
149          throw new RuntimeException("Bad metadata plugin specified ("+pluginClassName+")",th);
150       }
151       
152       if (!(plugin instanceof VoResourcePlugin)) {
153          throw new RuntimeException("Bad metadata plugin specified ("+pluginClassName+") - does not implement VoResourcePlugin");
154       }
155       
156       return (VoResourcePlugin) plugin;
157       
158    }
159    
160    /*** Instantiates the class with the given name.  This is useful for things
161     * such as 'plugins', where a class name might be given in a configuration file.
162     * Rather messily throws Throwable because anything might have
163     * gone wrong in the constructor.
164     */
165    public static QueryableResourceReader createQueryablePlugin(String pluginClassName) {
166       
167       Object plugin = null;
168       
169       try {
170          log.debug("Creating Queryable Plugin '"+pluginClassName+"'");
171          
172          Class qClass = Class.forName(pluginClassName);
173        
174          /* NWW - interesting bug here.
175           original code used class.newInstance(); this method doesn't declare it throws InvocationTargetException,
176           however, this exception _is_ thrown if an exception is thrown by the constructor (as is often the case at the moment)
177           worse, as InvocatioinTargetException is a checked exception, the compiler rejects code with a catch clause for
178           invocationTargetExcetpion - as it thinks it cannot be thrown.
179           this means the exception boils out of the code, and is unstoppable - dodgy
180           work-around - use the equivalent methods on java.lang.reflect.Constructor - which do throw the correct exceptions */
181          
182          Constructor constr = qClass.getConstructor(new Class[] { });
183          plugin = constr.newInstance(new Object[] { } );
184          
185       }
186       catch (ClassNotFoundException cnfe) {
187          throw new RuntimeException("Could not find metadata plugin class "+pluginClassName);
188       }
189       catch (NoSuchMethodException nsme) {
190          throw new RuntimeException("Bad metadata plugin specified ("+pluginClassName+") - has no zero-argument constructor");
191       }
192       catch (Throwable th) {
193          throw new RuntimeException("Bad metadata plugin specified ("+pluginClassName+")",th);
194       }
195       
196       if (!(plugin instanceof VoResourcePlugin)) {
197          throw new RuntimeException("Bad metadata plugin specified ("+pluginClassName+") - does not implement VoResourcePlugin");
198       }
199       
200       return (QueryableResourceReader) plugin;
201       
202    }
203    /***
204     * Clears the cache - useful to call before doing a set of operations, forces
205     * metadata to be refreshed from disk.  Not threadsafe...
206     */
207    public static void clearCache() {
208       cache = null;
209    }
210    
211    /***
212     * Make a VODescription document out of all the voResourcePlugins, returning an
213     * unvalidated string.  This means we can view the made (finsihed) docuemnt
214     * separate from the validating process. */
215    public static String makeVoDescription() throws IOException, MetadataException {
216 
217       //get plugin list from config - need to add a default to the common method...
218       Object[] plugins  = null;
219       try {
220          plugins = ConfigFactory.getCommonConfig().getProperties(RESOURCE_PLUGIN_KEY);
221       } catch (PropertyNotFoundException pnfe)
222       {
223          log.warn("No config found for resource plugins, key="+RESOURCE_PLUGIN_KEY);
224          
225          //for backwards compatibility, look for old datacenter.metadata.plugin
226          String s = ConfigFactory.getCommonConfig().getString("datacenter.metadata.plugin",null);
227          if (s != null) {
228             plugins = new String[] { s };
229          }
230       }
231 
232       //start the vodescription document
233       StringBuffer vod = new StringBuffer();
234       vod.append(VODESCRIPTION_ELEMENT+"\n");
235       boolean ceaDone = false; //backwards compatiblity marker to see if CeaResource has been done
236       
237       //loop through plugins adding each one's list of resources
238       if (plugins != null) {
239          for (int p = 0; p < plugins.length; p++) {
240             log.debug("Including Resource plugin "+plugins[p].toString());
241 
242             //make plugin
243             VoResourcePlugin plugin = createVoResourcePlugin(plugins[p].toString());
244             
245             checkAndAppendResource(vod, plugin);
246             
247             if (plugin instanceof CeaResources) { ceaDone = true; }
248          }
249       }
250 
251       //add the standard ones - cea, cone etc
252       if (!ceaDone) { checkAndAppendResource(vod, new CeaResources()); }
253       checkAndAppendResource(vod, new ConeResources());
254 //      addResources(vod, new SkyNodeResourceServer());
255       
256       //finish vod element
257       vod.append(VODESCRIPTION_ELEMENT_END);
258 
259       return vod.toString();
260    }
261 
262    public static void checkAndAppendResource(StringBuffer vod, VoResourcePlugin plugin) throws MetadataException, IOException {
263 
264       //get resources from plugin
265       String resources = plugin.getVoResource();
266 
267       try {
268          validateDescription(VODESCRIPTION_ELEMENT+resources+VODESCRIPTION_ELEMENT_END);
269       
270          vod.append(resources+"\n\n");
271       }
272       catch (SAXException e) {
273   //       throw new MetadataException("Plugin "+plugin.getClass()+" generated invalid XML ",e);
274     
275          log.error("Plugin "+plugin.getClass()+" generated invalid XML ",e);
276          vod.append(resources+"\n\n");
277       }
278    }
279 
280    
281    /***
282     * Returns the resource element of the given type eg 'AuthorityID'.
283     * Matches the given string against the attribute 'xsi:type' of the elements
284     * named 'Resource'
285     */
286    public static Element getResource(String type) throws IOException {
287       NodeList resources = getVoDescription().getElementsByTagName("Resource");
288       
289       for (int i = 0; i < resources.getLength(); i++) {
290          Element resource = (Element) resources.item(i);
291          if (resource.getAttribute("xsi:type").equals(type)) {
292             return resource;
293          }
294       }
295       return null; //not found
296    }
297 
298    /***
299     * Sends the voDescription to the registry, returning list of Registries that
300     * it was sent to
301     */
302    public static String[] pushToRegistry() throws IOException, RegistryException {
303       RegistryAdminService service = RegistryDelegateFactory.createAdmin();
304       service.update(getVoDescription());
305       return new String[] { ConfigFactory.getCommonConfig().getString(RegistryDelegateFactory.ADMIN_URL_PROPERTY) };
306    }
307 
308    /***
309     * Sends the voDescription to the given registry URL, returning list of Registries that
310     * it was sent to
311     */
312    public static void pushToRegistry(URL targetRegistry) throws IOException, RegistryException {
313       RegistryAdminService service = RegistryDelegateFactory.createAdmin(targetRegistry);
314       service.update(getVoDescription());
315    }
316 
317    /***
318     * for quick tests etc
319     */
320    public static void main(String[] args) throws RegistryException, IOException
321    {
322       SampleStarsPlugin.initConfig();
323 //    ConfigFactory.getCommonConfig().setProperty("datacenter.url","http://localhost:8080");
324       VoDescriptionServer.pushToRegistry(new URL("http://hydra.star.le.ac.uk:8080/astrogrid-registry/services/AdminService"));
325    }
326 }
327 
328 
329 
330 
331 
332 
333 
334