1 /*******************************************************************************
\r
2 * Copyright (c) 2007, 2010 Association for Decentralized Information Management
\r
3 * in Industry THTH ry.
\r
4 * All rights reserved. This program and the accompanying materials
\r
5 * are made available under the terms of the Eclipse Public License v1.0
\r
6 * which accompanies this distribution, and is available at
\r
7 * http://www.eclipse.org/legal/epl-v10.html
\r
10 * VTT Technical Research Centre of Finland - initial API and implementation
\r
11 *******************************************************************************/
\r
12 package org.simantics.db.services.adaption;
\r
14 import java.io.File;
\r
15 import java.io.StringReader;
\r
16 import java.net.URL;
\r
17 import java.util.ArrayList;
\r
18 import java.util.Collection;
\r
19 import java.util.HashMap;
\r
20 import java.util.List;
\r
21 import java.util.Map;
\r
23 import javax.xml.parsers.DocumentBuilder;
\r
24 import javax.xml.parsers.DocumentBuilderFactory;
\r
26 import org.eclipse.core.runtime.Path;
\r
27 import org.eclipse.core.runtime.Platform;
\r
28 import org.osgi.framework.Bundle;
\r
29 import org.osgi.framework.BundleContext;
\r
30 import org.simantics.db.ReadGraph;
\r
31 import org.simantics.db.Resource;
\r
32 import org.simantics.db.Session;
\r
33 import org.simantics.db.adaption.Adapter;
\r
34 import org.simantics.db.adaption.AdapterInstaller;
\r
35 import org.simantics.db.adaption.AdaptionService;
\r
36 import org.simantics.db.common.request.ReadRequest;
\r
37 import org.simantics.db.exception.DatabaseException;
\r
38 import org.simantics.db.services.adaption.reflection.AdaptingDynamicAdapter2;
\r
39 import org.simantics.db.services.adaption.reflection.AtMostOneRelatedResource2;
\r
40 import org.simantics.db.services.adaption.reflection.ConstantAdapter;
\r
41 import org.simantics.db.services.adaption.reflection.GraphObject2;
\r
42 import org.simantics.db.services.adaption.reflection.IDynamicAdapter2;
\r
43 import org.simantics.db.services.adaption.reflection.OrderedSetResources2;
\r
44 import org.simantics.db.services.adaption.reflection.ReflectionAdapter2;
\r
45 import org.simantics.db.services.adaption.reflection.RelatedResources2;
\r
46 import org.simantics.db.services.adaption.reflection.SingleRelatedResource2;
\r
47 import org.simantics.db.services.adaption.reflection.StaticMethodAdapter;
\r
48 import org.simantics.db.services.adaption.reflection.ThisResource2;
\r
49 import org.simantics.scl.reflection.OntologyVersions;
\r
50 import org.simantics.utils.FileUtils;
\r
51 import org.w3c.dom.DOMException;
\r
52 import org.w3c.dom.Document;
\r
53 import org.w3c.dom.NamedNodeMap;
\r
54 import org.w3c.dom.Node;
\r
55 import org.w3c.dom.NodeList;
\r
56 import org.xml.sax.ErrorHandler;
\r
57 import org.xml.sax.InputSource;
\r
58 import org.xml.sax.SAXException;
\r
59 import org.xml.sax.SAXParseException;
\r
61 public class AdapterRegistry2 {
\r
63 public static final String ADAPTERS_FILE = "adapters.xml";
\r
65 public static final String ADAPTERS = "adapters";
\r
66 public static final String ADAPTER = "adapter";
\r
67 public static final String TARGET = "target";
\r
68 public static final String BASE_TYPE = "baseType";
\r
69 public static final String TYPE = "type";
\r
70 public static final String RESOURCE = "resource";
\r
71 public static final String URI = "uri";
\r
72 public static final String INTERFACE = "interface";
\r
73 public static final String CLASS = "class";
\r
74 public static final String ADAPTER_CLASS = "adapterClass";
\r
75 public static final String CONTEXT_CLASS = "contextClass";
\r
76 public static final String INSTALLER = "installer";
\r
77 public static final String CONSTRUCTOR = "constructor";
\r
79 static private AdapterRegistry2 instance = new AdapterRegistry2();
\r
80 Collection<AdapterInstaller> installers = new ArrayList<AdapterInstaller>();
\r
81 Map<AdapterInstaller, String> installerSources = new HashMap<AdapterInstaller, String>();
\r
82 Collection<Exception> exceptions = new ArrayList<Exception>();
\r
84 public static AdapterRegistry2 getInstance() {
\r
88 private void addInstaller(AdapterInstaller installer, String sourceDesc) {
\r
89 installers.add(installer);
\r
90 installerSources.put(installer, sourceDesc);
\r
93 private void handleException(Exception e, String fileName) {
\r
94 System.err.println("At " + fileName);
\r
95 e.printStackTrace();
\r
98 private void handleException(Exception e, AdapterInstaller installer) {
\r
99 String desc = installerSources.get(installer);
\r
101 System.err.println("At " + desc);
\r
102 e.printStackTrace();
\r
105 private void handleAdaptersDocument(Loader b, Document doc, String fileName) {
\r
107 Node node = doc.getDocumentElement();
\r
108 if(node.getNodeName().equals(ADAPTERS)) {
\r
109 NodeList nodeList = node.getChildNodes();
\r
110 for(int i=0;i<nodeList.getLength();++i) {
\r
111 Node n = nodeList.item(i);
\r
112 if(n.getNodeName().equals(TARGET))
\r
113 handleTarget(b, n, fileName);
\r
114 else if(n.getNodeName().equals(INSTALLER))
\r
115 handleInstaller(b, n, fileName);
\r
118 } catch (Exception e) {
\r
119 handleException(e, fileName);
\r
123 private void handleTarget(Loader b, Node node, String fileName) {
\r
125 Class<?> interface_ =
\r
126 b.loadClass(node.getAttributes().getNamedItem("interface")
\r
128 NodeList nodeList = node.getChildNodes();
\r
129 for(int i=0;i<nodeList.getLength();++i) {
\r
130 Node n = nodeList.item(i);
\r
131 String nodeName = n.getNodeName();
\r
132 if(nodeName.equals(BASE_TYPE))
\r
133 handleBaseType(b, interface_, n, fileName);
\r
134 else if(nodeName.equals(TYPE))
\r
135 handleType(b, interface_, n, fileName);
\r
136 else if(nodeName.equals(ADAPTER))
\r
137 handleAdapter(b, interface_, n, fileName);
\r
138 else if(nodeName.equals(RESOURCE))
\r
139 handleResource(b, interface_, n, fileName);
\r
141 } catch (Exception e) {
\r
142 handleException(e, fileName);
\r
146 private void handleInstaller(Loader b, Node node, String fileName) {
\r
148 AdapterInstaller installer =
\r
149 ((Class<?>)b.loadClass(node.getAttributes().getNamedItem("class").getNodeValue()))
\r
150 .asSubclass(AdapterInstaller.class).newInstance();
\r
151 addInstaller(installer, fileName);
\r
152 } catch (Exception e) {
\r
153 handleException(e, fileName);
\r
157 private <T> void handleResource(final Loader b, final Class<T> interface_, final Node node, String fileName) {
\r
159 NamedNodeMap attr = node.getAttributes();
\r
160 final String uri = attr.getNamedItem(URI).getNodeValue();
\r
161 final String className = attr.getNamedItem(CLASS).getNodeValue();
\r
162 Node constructorNode = attr.getNamedItem(CONSTRUCTOR);
\r
163 final String constructor = constructorNode == null ? null : constructorNode.getNodeValue();
\r
164 // System.out.println("AdapterRegistry2.handleResource: " + b + " " + uri + " " + interface_);
\r
167 new AdapterInstaller() {
\r
170 public void install(ReadGraph g, AdaptionService service) throws Exception {
\r
171 Class<? extends T> clazz = b.loadClass(className).asSubclass(interface_);
\r
172 List<IDynamicAdapter2> parameters = readParameters(g, node, b);
\r
173 IDynamicAdapter2[] parameterArray =
\r
174 parameters.toArray(new IDynamicAdapter2[parameters.size()]);
\r
175 Resource r = g.getResource(uri);
\r
176 service.addInstanceAdapter(
\r
179 constructor == null
\r
180 ? new ReflectionAdapter2<T>(clazz, parameterArray)
\r
181 : new StaticMethodAdapter<T>(clazz, constructor, parameterArray));
\r
185 } catch (Exception e) {
\r
186 handleException(e, fileName);
\r
190 private <T> void handleType(final Loader b, final Class<T> interface_, final Node node, String fileName) {
\r
192 final NamedNodeMap attr = node.getAttributes();
\r
193 final String uri = attr.getNamedItem(URI).getNodeValue();
\r
194 Node constructorNode = attr.getNamedItem(CONSTRUCTOR);
\r
195 final String constructor = constructorNode == null ? null : constructorNode.getNodeValue();
\r
196 //System.out.println("AdapterRegistry2.handleType: " + b + " " + uri + " " + interface_);
\r
198 new AdapterInstaller() {
\r
201 public void install(ReadGraph g, AdaptionService service) throws Exception {
\r
203 Class<? extends T> clazz =
\r
204 ((Class<?>)b.loadClass(attr.getNamedItem(CLASS).getNodeValue()))
\r
205 .asSubclass(interface_);
\r
206 List<IDynamicAdapter2> parameters = readParameters(g, node, b);
\r
207 IDynamicAdapter2[] parameterArray =
\r
208 parameters.toArray(new IDynamicAdapter2[parameters.size()]);
\r
209 service.addAdapter(
\r
210 g.getResource(uri),
\r
212 constructor == null
\r
213 ? new ReflectionAdapter2<T>(clazz, parameterArray)
\r
214 : new StaticMethodAdapter<T>(clazz, constructor, parameterArray));
\r
216 System.err.println("Failed to adapt "+interface_.getName());
\r
218 } catch(RuntimeException t) {
\r
219 System.err.println("Failed to adapt "+interface_.getName());
\r
225 } catch (Exception e) {
\r
226 e.printStackTrace();
\r
227 handleException(e, fileName);
\r
231 private List<IDynamicAdapter2> readParameters(ReadGraph g, Node node, Loader b) throws DatabaseException, DOMException, ClassNotFoundException {
\r
232 NodeList nodeList = node.getChildNodes();
\r
233 ArrayList<IDynamicAdapter2> parameters = new ArrayList<IDynamicAdapter2>();
\r
234 for(int i=0;i<nodeList.getLength();++i) {
\r
235 Node n = nodeList.item(i);
\r
236 if(n.getNodeType() == Node.ELEMENT_NODE) {
\r
237 NamedNodeMap attr = n.getAttributes();
\r
238 IDynamicAdapter2 da = null;
\r
239 if(n.getNodeName().equals("this"))
\r
240 da = new ThisResource2();
\r
241 else if(n.getNodeName().equals("graph"))
\r
242 da = new GraphObject2();
\r
243 else if(n.getNodeName().equals("bundle")) {
\r
244 String bundleId = null;
\r
245 Node fc = n.getFirstChild();
\r
247 bundleId = fc.getNodeValue();
\r
248 if (bundleId == null) {
\r
249 da = new ConstantAdapter(Bundle.class, b.getBundle());
\r
251 Bundle ob = Platform.getBundle(bundleId);
\r
253 da = new ConstantAdapter(Bundle.class, ob);
\r
255 throw new DOMException(DOMException.NOT_FOUND_ERR, "bundle '" + bundleId + "' not found");
\r
258 } else if(n.getNodeName().equals("related"))
\r
259 da = new RelatedResources2(
\r
260 g.getResource(attr.getNamedItem("uri").getNodeValue()));
\r
261 else if(n.getNodeName().equals("orderedSet"))
\r
262 da = new OrderedSetResources2(
\r
263 g.getResource(attr.getNamedItem("uri").getNodeValue()));
\r
264 else if(n.getNodeName().equals("single"))
\r
265 da = new SingleRelatedResource2(
\r
266 g.getResource(attr.getNamedItem("uri").getNodeValue()));
\r
267 else if(n.getNodeName().equals("atMostOne"))
\r
268 da = new AtMostOneRelatedResource2(
\r
269 g.getResource(attr.getNamedItem("uri").getNodeValue()));
\r
270 else if(n.getNodeName().equals("string"))
\r
271 da = new ConstantAdapter(String.class, n.getFirstChild().getNodeValue());
\r
273 Node toNode = attr.getNamedItem("to");
\r
274 if(toNode != null) {
\r
275 String to = toNode.getNodeValue();
\r
276 da = new AdaptingDynamicAdapter2(da, b.loadClass(to));
\r
279 parameters.add(da);
\r
285 private <T> void handleAdapter(final Loader b, final Class<T> interface_, Node node, String fileName) {
\r
287 NamedNodeMap attr = node.getAttributes();
\r
288 final String uri = attr.getNamedItem(URI).getNodeValue();
\r
289 final String clazz = attr.getNamedItem(ADAPTER_CLASS).getNodeValue();
\r
291 Node contextNode = attr.getNamedItem(CONTEXT_CLASS);
\r
292 final Class<?> contextClass = contextNode != null ? b.loadClass(contextNode.getNodeValue()) : Resource.class;
\r
294 //System.out.println("AdapterRegistry2.handleAdapter: " + b + " " + uri + " " + interface_ + ", class=" + clazz);
\r
296 new AdapterInstaller() {
\r
298 @SuppressWarnings("unchecked")
\r
300 public void install(ReadGraph g, AdaptionService service) throws Exception {
\r
301 service.addAdapter(
\r
302 g.getResource(uri),
\r
305 ((Class<?>)b.loadClass(clazz))
\r
306 .asSubclass(Adapter.class).newInstance());
\r
310 } catch (Exception e) {
\r
311 handleException(e, fileName);
\r
315 private <T> void handleBaseType(Loader b, final Class<T> interface_, Node node, String fileName) {
\r
317 NamedNodeMap attr = node.getAttributes();
\r
318 final String uri = attr.getNamedItem(URI).getNodeValue();
\r
320 new AdapterInstaller() {
\r
323 public void install(ReadGraph g, AdaptionService service) throws Exception {
\r
324 service.declareAdapter(
\r
325 g.getResource(uri),
\r
330 } catch (Exception e) {
\r
331 handleException(e, fileName);
\r
335 public void updateAdaptionService(Session s, final AdaptionService service) throws DatabaseException {
\r
336 s.syncRequest(new ReadRequest() {
\r
338 public void run(ReadGraph g) {
\r
339 for(AdapterInstaller t : installers) {
\r
341 t.install(g, service);
\r
342 } catch (Exception e) {
\r
343 AdapterRegistry2.this.handleException(e, t);
\r
350 public void initialize(ClassLoader b, String schemaURL, File[] files) {
\r
354 DocumentBuilderFactory factory =
\r
355 DocumentBuilderFactory.newInstance();
\r
357 if(schemaURL != null) {
\r
359 factory.setValidating(true);
\r
360 factory.setAttribute(
\r
361 "http://java.sun.com/xml/jaxp/properties/schemaLanguage",
\r
362 "http://www.w3.org/2001/XMLSchema");
\r
363 factory.setAttribute(
\r
364 "http://java.sun.com/xml/jaxp/properties/schemaSource", schemaURL);
\r
368 // TODO Listen bundles (install/uninstall)
\r
369 if (exceptions.isEmpty())
\r
370 for (final File f : files) {
\r
371 // String fileName = new Path(b.getLocation()).append(file.getPath()).toString();
\r
373 DocumentBuilder builder = factory.newDocumentBuilder();
\r
374 builder.setErrorHandler(new ErrorHandler() {
\r
377 public void error(SAXParseException exception)
\r
378 throws SAXException {
\r
379 // TODO Put this error somewhere
\r
380 System.err.println("Parse error at " + f.getAbsolutePath() +
\r
381 // + b.getSymbolicName() + "/adapters.xml" +
\r
382 " line " + exception.getLineNumber() +
\r
383 " column " + exception.getColumnNumber() + ":");
\r
384 System.err.println(exception.getMessage());
\r
388 public void fatalError(SAXParseException exception)
\r
389 throws SAXException {
\r
394 public void warning(SAXParseException exception)
\r
395 throws SAXException {
\r
400 //System.out.println("bundle=" + b.getSymbolicName());
\r
401 Document doc = builder.parse(f);
\r
402 handleAdaptersDocument(loader(b), doc, f.getAbsolutePath());
\r
403 } catch (Exception e) {
\r
404 handleException(e, f.getAbsolutePath());
\r
408 } catch (Exception e) {
\r
409 handleException(e, "(no file name available)");
\r
414 public void initialize(BundleContext context) {
\r
418 DocumentBuilderFactory factory =
\r
419 DocumentBuilderFactory.newInstance();
\r
420 factory.setValidating(true);
\r
421 factory.setAttribute(
\r
422 "http://java.sun.com/xml/jaxp/properties/schemaLanguage",
\r
423 "http://www.w3.org/2001/XMLSchema");
\r
424 factory.setAttribute(
\r
425 "http://java.sun.com/xml/jaxp/properties/schemaSource",
\r
426 context.getBundle().getResource("adapters.xsd").toString());
\r
428 // TODO Listen bundles (install/uninstall)
\r
429 if (exceptions.isEmpty())
\r
430 for (final Bundle b : context.getBundles()) {
\r
431 URL file = b.getEntry(ADAPTERS_FILE);
\r
432 if (file != null) {
\r
433 String fileName = new Path(b.getLocation()).append(file.getPath()).toString();
\r
435 DocumentBuilder builder = factory.newDocumentBuilder();
\r
436 builder.setErrorHandler(new ErrorHandler() {
\r
439 public void error(SAXParseException exception)
\r
440 throws SAXException {
\r
441 // TODO Put this error somewhere
\r
442 System.err.println("Parse error at "
\r
443 + b.getSymbolicName() + "/adapters.xml" +
\r
444 " line " + exception.getLineNumber() +
\r
445 " column " + exception.getColumnNumber() + ":");
\r
446 System.err.println(exception.getMessage());
\r
450 public void fatalError(SAXParseException exception)
\r
451 throws SAXException {
\r
456 public void warning(SAXParseException exception)
\r
457 throws SAXException {
\r
463 //System.out.println("bundle=" + b.getSymbolicName());
\r
464 String text = FileUtils.getContents(file);
\r
465 text = OntologyVersions.getInstance().currentVersion(text);
\r
466 StringReader reader = new StringReader( text );
\r
467 InputSource inputSource = new InputSource( reader );
\r
468 Document doc = builder.parse( inputSource );
\r
470 handleAdaptersDocument(loader(b), doc, fileName);
\r
471 } catch (Exception e) {
\r
472 handleException(e, fileName);
\r
477 } catch (Exception e) {
\r
478 handleException(e, "(no file name available)");
\r
483 Class<?> loadClass(String name) throws ClassNotFoundException ;
\r
484 Bundle getBundle();
\r
487 private Loader loader(final Bundle b) {
\r
488 return new Loader() {
\r
491 public Class<?> loadClass(String name) throws ClassNotFoundException {
\r
492 return b.loadClass(name);
\r
496 public Bundle getBundle() {
\r
503 private Loader loader(final ClassLoader b) {
\r
504 return new Loader() {
\r
507 public Class<?> loadClass(String name) throws ClassNotFoundException {
\r
508 return b.loadClass(name);
\r
512 public Bundle getBundle() {
\r