1 /*******************************************************************************
2 * Copyright (c) 2007, 2010 Association for Decentralized Information Management
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License v1.0
6 * which accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.db.services.adaption;
15 import java.io.StringReader;
17 import java.util.ArrayList;
18 import java.util.Collection;
19 import java.util.List;
20 import java.util.concurrent.ConcurrentHashMap;
21 import java.util.concurrent.ExecutionException;
22 import java.util.concurrent.Future;
24 import javax.xml.parsers.DocumentBuilder;
25 import javax.xml.parsers.DocumentBuilderFactory;
27 import org.eclipse.core.runtime.Path;
28 import org.eclipse.core.runtime.Platform;
29 import org.osgi.framework.Bundle;
30 import org.osgi.framework.BundleContext;
31 import org.simantics.db.ReadGraph;
32 import org.simantics.db.Resource;
33 import org.simantics.db.Session;
34 import org.simantics.db.adaption.Adapter;
35 import org.simantics.db.adaption.AdapterInstaller;
36 import org.simantics.db.adaption.AdaptionService;
37 import org.simantics.db.common.request.ReadRequest;
38 import org.simantics.db.exception.DatabaseException;
39 import org.simantics.db.request.Read;
40 import org.simantics.db.services.adaption.reflection.AdaptingDynamicAdapter2;
41 import org.simantics.db.services.adaption.reflection.AtMostOneRelatedResource2;
42 import org.simantics.db.services.adaption.reflection.ConstantAdapter;
43 import org.simantics.db.services.adaption.reflection.GraphObject2;
44 import org.simantics.db.services.adaption.reflection.IDynamicAdapter2;
45 import org.simantics.db.services.adaption.reflection.OrderedSetResources2;
46 import org.simantics.db.services.adaption.reflection.ReflectionAdapter2;
47 import org.simantics.db.services.adaption.reflection.RelatedResources2;
48 import org.simantics.db.services.adaption.reflection.SingleRelatedResource2;
49 import org.simantics.db.services.adaption.reflection.StaticMethodAdapter;
50 import org.simantics.db.services.adaption.reflection.ThisResource2;
51 import org.simantics.scl.reflection.OntologyVersions;
52 import org.simantics.utils.FileUtils;
53 import org.simantics.utils.threads.ThreadUtils;
54 import org.slf4j.Logger;
55 import org.slf4j.LoggerFactory;
56 import org.w3c.dom.DOMException;
57 import org.w3c.dom.Document;
58 import org.w3c.dom.NamedNodeMap;
59 import org.w3c.dom.Node;
60 import org.w3c.dom.NodeList;
61 import org.xml.sax.ErrorHandler;
62 import org.xml.sax.InputSource;
63 import org.xml.sax.SAXException;
64 import org.xml.sax.SAXParseException;
66 public class AdapterRegistry2 {
68 private static final Logger LOGGER = LoggerFactory.getLogger(AdapterRegistry2.class);
70 public static final String ADAPTERS_FILE = "adapters.xml";
72 public static final String ADAPTERS = "adapters";
73 public static final String ADAPTER = "adapter";
74 public static final String TARGET = "target";
75 public static final String BASE_TYPE = "baseType";
76 public static final String TYPE = "type";
77 public static final String RESOURCE = "resource";
78 public static final String URI = "uri";
79 public static final String INTERFACE = "interface";
80 public static final String CLASS = "class";
81 public static final String ADAPTER_CLASS = "adapterClass";
82 public static final String CONTEXT_CLASS = "contextClass";
83 public static final String INSTALLER = "installer";
84 public static final String CONSTRUCTOR = "constructor";
86 static private AdapterRegistry2 instance = new AdapterRegistry2();
87 ConcurrentHashMap<AdapterInstaller, String> installerSources = new ConcurrentHashMap<>();
88 Collection<Exception> exceptions = new ArrayList<Exception>();
90 public static AdapterRegistry2 getInstance() {
94 private void addInstaller(AdapterInstaller installer, String sourceDesc) {
95 installerSources.put(installer, sourceDesc);
98 private static void handleException(Exception e, String fileName) {
99 System.err.println("At " + fileName);
103 private void handleException(Exception e, AdapterInstaller installer) {
104 String desc = installerSources.get(installer);
106 System.err.println("At " + desc);
110 private void handleAdaptersDocument(Loader b, Document doc, String fileName) {
112 Node node = doc.getDocumentElement();
113 if(node.getNodeName().equals(ADAPTERS)) {
114 NodeList nodeList = node.getChildNodes();
115 for(int i=0;i<nodeList.getLength();++i) {
116 Node n = nodeList.item(i);
117 if(n.getNodeName().equals(TARGET))
118 handleTarget(b, n, fileName);
119 else if(n.getNodeName().equals(INSTALLER))
120 handleInstaller(b, n, fileName);
123 } catch (Exception e) {
124 handleException(e, fileName);
128 private void handleTarget(Loader b, Node node, String fileName) {
130 Class<?> interface_ =
131 b.loadClass(node.getAttributes().getNamedItem("interface")
133 NodeList nodeList = node.getChildNodes();
134 for(int i=0;i<nodeList.getLength();++i) {
135 Node n = nodeList.item(i);
136 String nodeName = n.getNodeName();
137 if(nodeName.equals(BASE_TYPE))
138 handleBaseType(b, interface_, n, fileName);
139 else if(nodeName.equals(TYPE))
140 handleType(b, interface_, n, fileName);
141 else if(nodeName.equals(ADAPTER))
142 handleAdapter(b, interface_, n, fileName);
143 else if(nodeName.equals(RESOURCE))
144 handleResource(b, interface_, n, fileName);
146 } catch (Exception e) {
147 handleException(e, fileName);
151 private void handleInstaller(Loader b, Node node, String fileName) {
153 AdapterInstaller installer =
154 ((Class<?>)b.loadClass(node.getAttributes().getNamedItem("class").getNodeValue()))
155 .asSubclass(AdapterInstaller.class).newInstance();
156 addInstaller(installer, fileName);
157 } catch (Exception e) {
158 handleException(e, fileName);
162 private <T> void handleResource(final Loader b, final Class<T> interface_, final Node node, String fileName) {
164 NamedNodeMap attr = node.getAttributes();
165 final String uri = attr.getNamedItem(URI).getNodeValue();
166 final String className = attr.getNamedItem(CLASS).getNodeValue();
167 Node constructorNode = attr.getNamedItem(CONSTRUCTOR);
168 final String constructor = constructorNode == null ? null : constructorNode.getNodeValue();
169 // System.out.println("AdapterRegistry2.handleResource: " + b + " " + uri + " " + interface_);
172 new AdapterInstaller() {
175 public void install(ReadGraph g, AdaptionService service) throws Exception {
176 Class<? extends T> clazz = b.loadClass(className).asSubclass(interface_);
177 List<IDynamicAdapter2> parameters = readParameters(g, node, b);
178 IDynamicAdapter2[] parameterArray =
179 parameters.toArray(new IDynamicAdapter2[parameters.size()]);
180 Resource r = g.getResource(uri);
181 service.addInstanceAdapter(
185 ? new ReflectionAdapter2<T>(clazz, parameterArray)
186 : new StaticMethodAdapter<T>(clazz, constructor, parameterArray));
190 } catch (Exception e) {
191 handleException(e, fileName);
195 private <T> void handleType(final Loader b, final Class<T> interface_, final Node node, String fileName) {
197 final NamedNodeMap attr = node.getAttributes();
198 final String uri = attr.getNamedItem(URI).getNodeValue();
199 Node constructorNode = attr.getNamedItem(CONSTRUCTOR);
200 final String constructor = constructorNode == null ? null : constructorNode.getNodeValue();
201 //System.out.println("AdapterRegistry2.handleType: " + b + " " + uri + " " + interface_);
203 new AdapterInstaller() {
206 public void install(ReadGraph g, AdaptionService service) throws Exception {
208 Class<? extends T> clazz =
209 ((Class<?>)b.loadClass(attr.getNamedItem(CLASS).getNodeValue()))
210 .asSubclass(interface_);
211 List<IDynamicAdapter2> parameters = readParameters(g, node, b);
212 IDynamicAdapter2[] parameterArray =
213 parameters.toArray(new IDynamicAdapter2[parameters.size()]);
218 ? new ReflectionAdapter2<T>(clazz, parameterArray)
219 : new StaticMethodAdapter<T>(clazz, constructor, parameterArray));
221 System.err.println("Failed to adapt "+interface_.getName());
223 } catch(RuntimeException t) {
224 System.err.println("Failed to adapt "+interface_.getName());
230 } catch (Exception e) {
232 handleException(e, fileName);
236 private List<IDynamicAdapter2> readParameters(ReadGraph g, Node node, Loader b) throws DatabaseException, DOMException, ClassNotFoundException {
237 NodeList nodeList = node.getChildNodes();
238 ArrayList<IDynamicAdapter2> parameters = new ArrayList<IDynamicAdapter2>();
239 for(int i=0;i<nodeList.getLength();++i) {
240 Node n = nodeList.item(i);
241 if(n.getNodeType() == Node.ELEMENT_NODE) {
242 NamedNodeMap attr = n.getAttributes();
243 IDynamicAdapter2 da = null;
244 if(n.getNodeName().equals("this"))
245 da = new ThisResource2();
246 else if(n.getNodeName().equals("graph"))
247 da = new GraphObject2();
248 else if(n.getNodeName().equals("bundle")) {
249 String bundleId = null;
250 Node fc = n.getFirstChild();
252 bundleId = fc.getNodeValue();
253 if (bundleId == null) {
254 da = new ConstantAdapter(Bundle.class, b.getBundle());
256 Bundle ob = Platform.getBundle(bundleId);
258 da = new ConstantAdapter(Bundle.class, ob);
260 throw new DOMException(DOMException.NOT_FOUND_ERR, "bundle '" + bundleId + "' not found");
263 } else if(n.getNodeName().equals("related"))
264 da = new RelatedResources2(
265 g.getResource(attr.getNamedItem("uri").getNodeValue()));
266 else if(n.getNodeName().equals("orderedSet"))
267 da = new OrderedSetResources2(
268 g.getResource(attr.getNamedItem("uri").getNodeValue()));
269 else if(n.getNodeName().equals("single"))
270 da = new SingleRelatedResource2(
271 g.getResource(attr.getNamedItem("uri").getNodeValue()));
272 else if(n.getNodeName().equals("atMostOne"))
273 da = new AtMostOneRelatedResource2(
274 g.getResource(attr.getNamedItem("uri").getNodeValue()));
275 else if(n.getNodeName().equals("string"))
276 da = new ConstantAdapter(String.class, n.getFirstChild().getNodeValue());
278 Node toNode = attr.getNamedItem("to");
280 String to = toNode.getNodeValue();
281 da = new AdaptingDynamicAdapter2(da, b.loadClass(to));
290 private <T> void handleAdapter(final Loader b, final Class<T> interface_, Node node, String fileName) {
292 NamedNodeMap attr = node.getAttributes();
293 final String uri = attr.getNamedItem(URI).getNodeValue();
294 final String clazz = attr.getNamedItem(ADAPTER_CLASS).getNodeValue();
296 Node contextNode = attr.getNamedItem(CONTEXT_CLASS);
297 final Class<?> contextClass = contextNode != null ? b.loadClass(contextNode.getNodeValue()) : Resource.class;
299 //System.out.println("AdapterRegistry2.handleAdapter: " + b + " " + uri + " " + interface_ + ", class=" + clazz);
301 new AdapterInstaller() {
303 @SuppressWarnings("unchecked")
305 public void install(ReadGraph g, AdaptionService service) throws Exception {
310 ((Class<?>)b.loadClass(clazz))
311 .asSubclass(Adapter.class).newInstance());
315 } catch (Exception e) {
316 handleException(e, fileName);
320 private <T> void handleBaseType(Loader b, final Class<T> interface_, Node node, String fileName) {
322 NamedNodeMap attr = node.getAttributes();
323 final String uri = attr.getNamedItem(URI).getNodeValue();
325 new AdapterInstaller() {
328 public void install(ReadGraph g, AdaptionService service) throws Exception {
329 service.declareAdapter(
335 } catch (Exception e) {
336 handleException(e, fileName);
340 public void updateAdaptionService(Session s, final AdaptionService service) throws DatabaseException {
341 s.syncRequest(new Read() {
343 public Object perform(ReadGraph g) {
344 for(AdapterInstaller t : installerSources.keySet()) {
346 t.install(g, service);
347 } catch (Exception e) {
348 AdapterRegistry2.this.handleException(e, t);
356 public void initialize(ClassLoader b, String schemaURL, File[] files) {
360 DocumentBuilderFactory factory =
361 DocumentBuilderFactory.newInstance();
363 if(schemaURL != null && validateAgainstSchema()) {
365 factory.setValidating(true);
366 factory.setAttribute(
367 "http://java.sun.com/xml/jaxp/properties/schemaLanguage",
368 "http://www.w3.org/2001/XMLSchema");
369 factory.setAttribute(
370 "http://java.sun.com/xml/jaxp/properties/schemaSource", schemaURL);
374 // TODO Listen bundles (install/uninstall)
375 if (exceptions.isEmpty())
376 for (final File f : files) {
377 // String fileName = new Path(b.getLocation()).append(file.getPath()).toString();
379 DocumentBuilder builder = factory.newDocumentBuilder();
380 builder.setErrorHandler(new ErrorHandler() {
383 public void error(SAXParseException exception)
384 throws SAXException {
385 // TODO Put this error somewhere
386 System.err.println("Parse error at " + f.getAbsolutePath() +
387 // + b.getSymbolicName() + "/adapters.xml" +
388 " line " + exception.getLineNumber() +
389 " column " + exception.getColumnNumber() + ":");
390 System.err.println(exception.getMessage());
394 public void fatalError(SAXParseException exception)
395 throws SAXException {
400 public void warning(SAXParseException exception)
401 throws SAXException {
406 //System.out.println("bundle=" + b.getSymbolicName());
407 Document doc = builder.parse(f);
408 handleAdaptersDocument(loader(b), doc, f.getAbsolutePath());
409 } catch (Exception e) {
410 handleException(e, f.getAbsolutePath());
414 } catch (Exception e) {
415 handleException(e, "(no file name available)");
420 private boolean validateAgainstSchema() {
421 return Platform.inDevelopmentMode();
424 public void initialize(BundleContext context) {
425 LOGGER.info("Initializing");
428 DocumentBuilderFactory factory =
429 DocumentBuilderFactory.newInstance();
431 if (validateAgainstSchema()) {
432 factory.setValidating(true);
433 factory.setAttribute(
434 "http://java.sun.com/xml/jaxp/properties/schemaLanguage",
435 "http://www.w3.org/2001/XMLSchema");
436 factory.setAttribute(
437 "http://java.sun.com/xml/jaxp/properties/schemaSource",
438 context.getBundle().getResource("adapters.xsd").toString());
441 // TODO Listen bundles (install/uninstall)
442 List<Future<?>> waitFor = new ArrayList<>();
443 if (exceptions.isEmpty())
444 for (final Bundle b : context.getBundles()) {
445 Future<?> submit = ThreadUtils.getNonBlockingWorkExecutor().submit(() -> {
446 URL file = b.getEntry(ADAPTERS_FILE);
448 String fileName = new Path(b.getLocation()).append(file.getPath()).toString();
450 DocumentBuilder builder = factory.newDocumentBuilder();
451 builder.setErrorHandler(new ErrorHandler() {
454 public void error(SAXParseException exception) throws SAXException {
455 // TODO Put this error somewhere
456 System.err.println("Parse error at " + b.getSymbolicName() + "/adapters.xml"
457 + " line " + exception.getLineNumber() + " column "
458 + exception.getColumnNumber() + ":");
459 System.err.println(exception.getMessage());
463 public void fatalError(SAXParseException exception) throws SAXException {
468 public void warning(SAXParseException exception) throws SAXException {
474 // System.out.println("bundle=" + b.getSymbolicName());
475 String text = FileUtils.getContents(file);
476 text = OntologyVersions.getInstance().currentVersion(text);
477 StringReader reader = new StringReader(text);
478 InputSource inputSource = new InputSource(reader);
479 Document doc = builder.parse(inputSource);
481 handleAdaptersDocument(loader(b), doc, fileName);
482 } catch (Exception e) {
483 handleException(e, fileName);
490 // Let's wait in here
491 waitFor.forEach(f -> {
494 } catch (InterruptedException | ExecutionException e) {
495 LOGGER.error("Could not wait adapters to load", e);
498 LOGGER.info("Adapters installed");
499 } catch (Exception e) {
500 handleException(e, "(no file name available)");
505 Class<?> loadClass(String name) throws ClassNotFoundException ;
509 private Loader loader(final Bundle b) {
510 return new Loader() {
513 public Class<?> loadClass(String name) throws ClassNotFoundException {
514 return b.loadClass(name);
518 public Bundle getBundle() {
525 private Loader loader(final ClassLoader b) {
526 return new Loader() {
529 public Class<?> loadClass(String name) throws ClassNotFoundException {
530 return b.loadClass(name);
534 public Bundle getBundle() {