View Javadoc

1   /*
2    * Copyright (c) 2007, James Leigh All rights reserved.
3    * 
4    * Redistribution and use in source and binary forms, with or without
5    * modification, are permitted provided that the following conditions are met:
6    * 
7    * - Redistributions of source code must retain the above copyright notice, this
8    *   list of conditions and the following disclaimer.
9    * - Redistributions in binary form must reproduce the above copyright notice,
10   *   this list of conditions and the following disclaimer in the documentation
11   *   and/or other materials provided with the distribution. 
12   * - Neither the name of the openrdf.org nor the names of its contributors may
13   *   be used to endorse or promote products derived from this software without
14   *   specific prior written permission.
15   * 
16   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26   * POSSIBILITY OF SUCH DAMAGE.
27   * 
28   */
29  package org.openrdf.elmo;
30  
31  import static java.util.Collections.unmodifiableList;
32  import static java.util.Collections.unmodifiableMap;
33  import static java.util.Collections.unmodifiableSet;
34  
35  import java.io.IOException;
36  import java.io.InputStream;
37  import java.net.URL;
38  import java.net.URLClassLoader;
39  import java.util.ArrayList;
40  import java.util.Enumeration;
41  import java.util.HashMap;
42  import java.util.LinkedHashSet;
43  import java.util.List;
44  import java.util.Map;
45  import java.util.Set;
46  import java.util.Vector;
47  
48  import javax.xml.namespace.QName;
49  
50  /**
51   * Defines the Scope of an ElmoManager and its factory. This includes roles,
52   * literals, factories, datasets, and contexts.
53   * 
54   * @author James Leigh
55   * 
56   */
57  public class ElmoModule {
58  	public static class Association {
59  		private Class<?> javaClass;
60  
61  		private String rdfType;
62  
63  		private Association(Class<?> javaClass, String rdfType) {
64  			this.javaClass = javaClass;
65  			this.rdfType = rdfType;
66  		}
67  
68  		public Class<?> getJavaClass() {
69  			return javaClass;
70  		}
71  
72  		public String getRdfType() {
73  			return rdfType;
74  		}
75  
76  		public String toString() {
77  			return javaClass.getName() + "=" + rdfType;
78  		}
79  	}
80  
81  	private static class CombinedClassLoader extends ClassLoader {
82  		private ClassLoader alt;
83  
84  		public CombinedClassLoader(ClassLoader parent, ClassLoader alt) {
85  			super(parent);
86  			this.alt = alt;
87  		}
88  
89  		@Override
90  		public URL getResource(String name) {
91  			URL resource = super.getResource(name);
92  			if (resource == null)
93  				return alt.getResource(name);
94  			return resource;
95  		}
96  
97  		@Override
98  		public InputStream getResourceAsStream(String name) {
99  			InputStream stream = super.getResourceAsStream(name);
100 			if (stream == null)
101 				return alt.getResourceAsStream(name);
102 			return stream;
103 		}
104 
105 		@Override
106 		public Enumeration<URL> getResources(String name) throws IOException {
107 			Vector<URL> list = new Vector<URL>();
108 			Enumeration<URL> e = super.getResources(name);
109 			while (e.hasMoreElements()) {
110 				list.add(e.nextElement());
111 			}
112 			e = alt.getResources(name);
113 			while (e.hasMoreElements()) {
114 				list.add(e.nextElement());
115 			}
116 			return list.elements();
117 		}
118 
119 		@Override
120 		public Class<?> loadClass(String name) throws ClassNotFoundException {
121 			try {
122 				return super.loadClass(name);
123 			} catch (ClassNotFoundException e) {
124 				try {
125 					return alt.loadClass(name);
126 				} catch (ClassNotFoundException e2) {
127 					throw e;
128 				}
129 			}
130 		}
131 
132 	}
133 
134 	private ClassLoader cl;
135 
136 	private List<Association> datatypes = new ArrayList<Association>();
137 
138 	private List<Association> concepts = new ArrayList<Association>();
139 
140 	private List<Association> behaviours = new ArrayList<Association>();
141 
142 	private List<Association> factories = new ArrayList<Association>();
143 
144 	private QName graph;
145 
146 	private Set<QName> includedGraphs = new LinkedHashSet<QName>();
147 
148 	private Map<URL, String> datasets = new HashMap<URL, String>();
149 
150 	private List<String> resources = new ArrayList<String>();
151 
152 	private List<URL> jars = new ArrayList<URL>();
153 
154 	private ClassLoader urlClassLoader;
155 
156 	public ElmoModule() {
157 		super();
158 		cl = Thread.currentThread().getContextClassLoader();
159 		if (cl == null) {
160 			cl = getClass().getClassLoader();
161 		}
162 	}
163 
164 	public ElmoModule(ClassLoader cl) {
165 		super();
166 		this.cl = cl;
167 	}
168 
169 	public synchronized ClassLoader getClassLoader() {
170 		if (urlClassLoader == null) {
171 			if (jars.isEmpty()) {
172 				urlClassLoader = cl;
173 			} else {
174 				URL[] array = jars.toArray(new URL[jars.size()]);
175 				urlClassLoader = new URLClassLoader(array, cl);
176 			}
177 		}
178 		return urlClassLoader;
179 	}
180 
181 	public QName getGraph() {
182 		return graph;
183 	}
184 
185 	/**
186 	 * Sets the primary graph of this module. This limits the readable scope to
187 	 * this and other included graphs and causes any add operations to be added
188 	 * to this graph.
189 	 * 
190 	 * @param graph
191 	 * @return this
192 	 */
193 	public ElmoModule setGraph(QName graph) {
194 		this.graph = graph;
195 		return this;
196 	}
197 
198 	@Deprecated
199 	public ElmoModule setContext(QName graph) {
200 		return setGraph(graph);
201 	}
202 
203 	/**
204 	 * Include all the information from the given module in this module.
205 	 * 
206 	 * @param module
207 	 *            to be included
208 	 * @return this
209 	 */
210 	public ElmoModule includeModule(ElmoModule module) {
211 		datatypes.addAll(module.datatypes);
212 		concepts.addAll(module.concepts);
213 		behaviours.addAll(module.behaviours);
214 		factories.addAll(module.factories);
215 		includedGraphs.add(module.graph);
216 		includedGraphs.addAll(module.includedGraphs);
217 		datasets.putAll(module.datasets);
218 		resources.addAll(module.resources);
219 		if (!module.jars.isEmpty()) {
220 			cl = new CombinedClassLoader(cl, module.getClassLoader());
221 		} else if (!cl.equals(module.cl)) {
222 			cl = new CombinedClassLoader(cl, module.cl);
223 		}
224 		return this;
225 	}
226 
227 	public Set<QName> getIncludedGraphs() {
228 		return unmodifiableSet(includedGraphs);
229 	}
230 
231 	public List<Association> getDatatypes() {
232 		return unmodifiableList(datatypes);
233 	}
234 
235 	/**
236 	 * Associates this datatype with the given uri within this factory.
237 	 * 
238 	 * @param type
239 	 *            serializable class
240 	 * @param datatype
241 	 *            URI
242 	 */
243 	public ElmoModule addDatatype(Class<?> type, String uri) {
244 		datatypes.add(new Association(type, uri));
245 		return this;
246 	}
247 
248 	@Deprecated
249 	public ElmoModule addLiteral(Class<?> type, String uri) {
250 		return addDatatype(type, uri);
251 	}
252 
253 	/**
254 	 * Associates this role with its default subject type.
255 	 * 
256 	 * @param role
257 	 *            concept or behaviour
258 	 */
259 	@Deprecated
260 	public ElmoModule addRole(Class<?> role) {
261 		if (role.isInterface()) {
262 			addConcept(role);
263 		} else {
264 			addBehaviour(role);
265 		}
266 		return this;
267 	}
268 
269 	/**
270 	 * Associates this role with the given subject type.
271 	 * 
272 	 * @param role
273 	 *            concept or behaviour
274 	 * @param type
275 	 *            URI
276 	 */
277 	@Deprecated
278 	public ElmoModule addRole(Class<?> role, String type) {
279 		if (role.isInterface()) {
280 			addConcept(role, type);
281 		} else {
282 			addBehaviour(role, type);
283 		}
284 		return this;
285 	}
286 
287 	public List<Association> getConcepts() {
288 		return unmodifiableList(concepts);
289 	}
290 
291 	/**
292 	 * Associates this concept with its default subject type.
293 	 * 
294 	 * @param concept
295 	 *            interface or class
296 	 */
297 	public ElmoModule addConcept(Class<?> concept) {
298 		concepts.add(new Association(concept, null));
299 		return this;
300 	}
301 
302 	/**
303 	 * Associates this concept with the given subject type.
304 	 * 
305 	 * @param concept
306 	 *            interface or class
307 	 * @param type
308 	 *            URI
309 	 */
310 	public ElmoModule addConcept(Class<?> concept, String type) {
311 		concepts.add(new Association(concept, type));
312 		return this;
313 	}
314 
315 	public List<Association> getBehaviours() {
316 		return unmodifiableList(behaviours);
317 	}
318 
319 	/**
320 	 * Associates this behaviour with its default subject type.
321 	 * 
322 	 * @param behaviour
323 	 *            class
324 	 */
325 	public ElmoModule addBehaviour(Class<?> behaviour) {
326 		behaviours.add(new Association(behaviour, null));
327 		return this;
328 	}
329 
330 	/**
331 	 * Associates this behaviour with the given subject type.
332 	 * 
333 	 * @param behaviour
334 	 *            class
335 	 * @param type
336 	 *            URI
337 	 */
338 	public ElmoModule addBehaviour(Class<?> behaviour, String type) {
339 		behaviours.add(new Association(behaviour, type));
340 		return this;
341 	}
342 
343 	public List<Association> getFactories() {
344 		return unmodifiableList(factories);
345 	}
346 
347 	/**
348 	 * Associates this factory with its default subject type.
349 	 * 
350 	 * @param factory
351 	 *            class
352 	 */
353 	public ElmoModule addFactory(Class<?> factory) {
354 		factories.add(new Association(factory, null));
355 		return this;
356 	}
357 
358 	/**
359 	 * Associates this factory with the given subject type.
360 	 * 
361 	 * @param factory
362 	 *            class
363 	 * @param type
364 	 *            URI
365 	 */
366 	public ElmoModule addFactory(Class<?> factory, String type) {
367 		factories.add(new Association(factory, type));
368 		return this;
369 	}
370 
371 	public Map<URL, String> getDatasets() {
372 		return unmodifiableMap(datasets);
373 	}
374 
375 	/**
376 	 * Marks this dataset to be loaded into the repository under a context of
377 	 * the same URL.
378 	 * 
379 	 * @param dataset
380 	 * @return this
381 	 */
382 	public ElmoModule addDataset(URL dataset) {
383 		return addDataset(dataset, dataset.toExternalForm());
384 	}
385 
386 	/**
387 	 * Marks this dataset to be loaded and replace any data in the given
388 	 * context.
389 	 * 
390 	 * @param dataset
391 	 * @param context
392 	 * @return this
393 	 */
394 	public ElmoModule addDataset(URL dataset, String context) {
395 		datasets.put(dataset, context);
396 		return this;
397 	}
398 
399 	public List<String> getResources() {
400 		return unmodifiableList(resources);
401 	}
402 
403 	/**
404 	 * Load a resource listing datasets - optionally assigned to a
405 	 * context
406 	 * 
407 	 * @param path
408 	 * @return
409 	 */
410 	public ElmoModule addResources(String path) {
411 		resources.add(path);
412 		return this;
413 	}
414 
415 	public List<URL> getJarFileUrls() {
416 		return unmodifiableList(jars);
417 	}
418 
419 	public ElmoModule addJarFileUrl(URL jarFile) {
420 		jars.add(jarFile);
421 		urlClassLoader = null;
422 		return this;
423 	}
424 
425 	@Override
426 	public int hashCode() {
427 		final int prime = 31;
428 		int result = 1;
429 		result = prime * result + ((cl == null) ? 0 : cl.hashCode());
430 		result = prime * result + ((graph == null) ? 0 : graph.hashCode());
431 		result = prime * result
432 				+ ((datasets == null) ? 0 : datasets.hashCode());
433 		result = prime * result
434 				+ ((factories == null) ? 0 : factories.hashCode());
435 		result = prime * result
436 				+ ((includedGraphs == null) ? 0 : includedGraphs.hashCode());
437 		result = prime * result + ((jars == null) ? 0 : jars.hashCode());
438 		result = prime * result
439 				+ ((datatypes == null) ? 0 : datatypes.hashCode());
440 		result = prime * result
441 				+ ((resources == null) ? 0 : resources.hashCode());
442 		result = prime * result
443 				+ ((concepts == null) ? 0 : concepts.hashCode());
444 		result = prime * result
445 				+ ((behaviours == null) ? 0 : behaviours.hashCode());
446 		result = prime * result
447 				+ ((urlClassLoader == null) ? 0 : urlClassLoader.hashCode());
448 		return result;
449 	}
450 
451 	@Override
452 	public boolean equals(Object obj) {
453 		if (this == obj)
454 			return true;
455 		if (obj == null)
456 			return false;
457 		if (getClass() != obj.getClass())
458 			return false;
459 		final ElmoModule other = (ElmoModule) obj;
460 		if (cl == null) {
461 			if (other.cl != null)
462 				return false;
463 		} else if (!cl.equals(other.cl))
464 			return false;
465 		if (graph == null) {
466 			if (other.graph != null)
467 				return false;
468 		} else if (!graph.equals(other.graph))
469 			return false;
470 		if (datasets == null) {
471 			if (other.datasets != null)
472 				return false;
473 		} else if (!datasets.equals(other.datasets))
474 			return false;
475 		if (factories == null) {
476 			if (other.factories != null)
477 				return false;
478 		} else if (!factories.equals(other.factories))
479 			return false;
480 		if (includedGraphs == null) {
481 			if (other.includedGraphs != null)
482 				return false;
483 		} else if (!includedGraphs.equals(other.includedGraphs))
484 			return false;
485 		if (jars == null) {
486 			if (other.jars != null)
487 				return false;
488 		} else if (!jars.equals(other.jars))
489 			return false;
490 		if (datatypes == null) {
491 			if (other.datatypes != null)
492 				return false;
493 		} else if (!datatypes.equals(other.datatypes))
494 			return false;
495 		if (resources == null) {
496 			if (other.resources != null)
497 				return false;
498 		} else if (!resources.equals(other.resources))
499 			return false;
500 		if (concepts == null) {
501 			if (other.concepts != null)
502 				return false;
503 		} else if (!concepts.equals(other.concepts))
504 			return false;
505 		if (behaviours == null) {
506 			if (other.behaviours != null)
507 				return false;
508 		} else if (!behaviours.equals(other.behaviours))
509 			return false;
510 		if (urlClassLoader == null) {
511 			if (other.urlClassLoader != null)
512 				return false;
513 		} else if (!urlClassLoader.equals(other.urlClassLoader))
514 			return false;
515 		return true;
516 	}
517 
518 	@Override
519 	public String toString() {
520 		if (graph != null)
521 			return graph.toString();
522 		Set<Package> pkg = new LinkedHashSet<Package>();
523 		for (Association concept : concepts) {
524 			pkg.add(concept.getJavaClass().getPackage());
525 		}
526 		return pkg.toString();
527 	}
528 }