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