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.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
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
131
132
133
134
135
136
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
175
176
177
178
179
180
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
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
226 String s = ConfigFactory.getCommonConfig().getString("datacenter.metadata.plugin",null);
227 if (s != null) {
228 plugins = new String[] { s };
229 }
230 }
231
232
233 StringBuffer vod = new StringBuffer();
234 vod.append(VODESCRIPTION_ELEMENT+"\n");
235 boolean ceaDone = false;
236
237
238 if (plugins != null) {
239 for (int p = 0; p < plugins.length; p++) {
240 log.debug("Including Resource plugin "+plugins[p].toString());
241
242
243 VoResourcePlugin plugin = createVoResourcePlugin(plugins[p].toString());
244
245 checkAndAppendResource(vod, plugin);
246
247 if (plugin instanceof CeaResources) { ceaDone = true; }
248 }
249 }
250
251
252 if (!ceaDone) { checkAndAppendResource(vod, new CeaResources()); }
253 checkAndAppendResource(vod, new ConeResources());
254
255
256
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
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
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;
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
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