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.exception.DatabaseException;
38 import org.simantics.db.request.Read;
39 import org.simantics.db.services.adaption.reflection.AdaptingDynamicAdapter2;
40 import org.simantics.db.services.adaption.reflection.AtMostOneRelatedResource2;
41 import org.simantics.db.services.adaption.reflection.ConstantAdapter;
42 import org.simantics.db.services.adaption.reflection.GraphObject2;
43 import org.simantics.db.services.adaption.reflection.IDynamicAdapter2;
44 import org.simantics.db.services.adaption.reflection.OrderedSetResources2;
45 import org.simantics.db.services.adaption.reflection.ReflectionAdapter2;
46 import org.simantics.db.services.adaption.reflection.RelatedResources2;
47 import org.simantics.db.services.adaption.reflection.SingleRelatedResource2;
48 import org.simantics.db.services.adaption.reflection.StaticMethodAdapter;
49 import org.simantics.db.services.adaption.reflection.ThisResource2;
50 import org.simantics.scl.reflection.OntologyVersions;
51 import org.simantics.utils.FileUtils;
52 import org.simantics.utils.threads.ThreadUtils;
53 import org.slf4j.Logger;
54 import org.slf4j.LoggerFactory;
55 import org.w3c.dom.DOMException;
56 import org.w3c.dom.Document;
57 import org.w3c.dom.NamedNodeMap;
58 import org.w3c.dom.Node;
59 import org.w3c.dom.NodeList;
60 import org.xml.sax.ErrorHandler;
61 import org.xml.sax.InputSource;
62 import org.xml.sax.SAXException;
63 import org.xml.sax.SAXParseException;
65 public class AdapterRegistry2 {
67 private static final Logger LOGGER = LoggerFactory.getLogger(AdapterRegistry2.class);
69 public static final String ADAPTERS_FILE = "adapters.xml";
71 public static final String ADAPTERS = "adapters";
72 public static final String ADAPTER = "adapter";
73 public static final String TARGET = "target";
74 public static final String BASE_TYPE = "baseType";
75 public static final String TYPE = "type";
76 public static final String RESOURCE = "resource";
77 public static final String URI = "uri";
78 public static final String INTERFACE = "interface";
79 public static final String CLASS = "class";
80 public static final String ADAPTER_CLASS = "adapterClass";
81 public static final String CONTEXT_CLASS = "contextClass";
82 public static final String INSTALLER = "installer";
83 public static final String CONSTRUCTOR = "constructor";
85 static private AdapterRegistry2 instance = new AdapterRegistry2();
86 ConcurrentHashMap<AdapterInstaller, String> installerSources = new ConcurrentHashMap<>();
87 Collection<Exception> exceptions = new ArrayList<Exception>();
89 public static AdapterRegistry2 getInstance() {
93 private void addInstaller(AdapterInstaller installer, String sourceDesc) {
94 installerSources.put(installer, sourceDesc);
97 private static void handleException(Exception e, String fileName) {
98 LOGGER.error("At {}", fileName, e);
101 private void handleException(Exception e, AdapterInstaller installer) {
102 String desc = installerSources.get(installer);
103 LOGGER.error("At {}, installer {}", desc, installer, e);
106 private void handleAdaptersDocument(Loader b, Document doc, String fileName) {
108 Node node = doc.getDocumentElement();
109 if(node.getNodeName().equals(ADAPTERS)) {
110 NodeList nodeList = node.getChildNodes();
111 for(int i=0;i<nodeList.getLength();++i) {
112 Node n = nodeList.item(i);
113 if(n.getNodeName().equals(TARGET))
114 handleTarget(b, n, fileName);
115 else if(n.getNodeName().equals(INSTALLER))
116 handleInstaller(b, n, fileName);
119 } catch (Exception e) {
120 handleException(e, fileName);
124 private void handleTarget(Loader b, Node node, String fileName) {
126 Class<?> interface_ =
127 b.loadClass(node.getAttributes().getNamedItem("interface")
129 NodeList nodeList = node.getChildNodes();
130 for(int i=0;i<nodeList.getLength();++i) {
131 Node n = nodeList.item(i);
132 String nodeName = n.getNodeName();
133 if(nodeName.equals(BASE_TYPE))
134 handleBaseType(b, interface_, n, fileName);
135 else if(nodeName.equals(TYPE))
136 handleType(b, interface_, n, fileName);
137 else if(nodeName.equals(ADAPTER))
138 handleAdapter(b, interface_, n, fileName);
139 else if(nodeName.equals(RESOURCE))
140 handleResource(b, interface_, n, fileName);
142 } catch (Exception e) {
143 handleException(e, fileName);
147 private void handleInstaller(Loader b, Node node, String fileName) {
149 AdapterInstaller installer =
150 ((Class<?>)b.loadClass(node.getAttributes().getNamedItem("class").getNodeValue()))
151 .asSubclass(AdapterInstaller.class).newInstance();
152 addInstaller(installer, fileName);
153 } catch (Exception e) {
154 handleException(e, fileName);
158 private <T> void handleResource(final Loader b, final Class<T> interface_, final Node node, String fileName) {
160 NamedNodeMap attr = node.getAttributes();
161 final String uri = attr.getNamedItem(URI).getNodeValue();
162 final String className = attr.getNamedItem(CLASS).getNodeValue();
163 Node constructorNode = attr.getNamedItem(CONSTRUCTOR);
164 final String constructor = constructorNode == null ? null : constructorNode.getNodeValue();
165 // System.out.println("AdapterRegistry2.handleResource: " + b + " " + uri + " " + interface_);
168 new AdapterInstaller() {
171 public void install(ReadGraph g, AdaptionService service) throws Exception {
172 Class<? extends T> clazz = b.loadClass(className).asSubclass(interface_);
173 List<IDynamicAdapter2> parameters = readParameters(g, node, b);
174 IDynamicAdapter2[] parameterArray =
175 parameters.toArray(new IDynamicAdapter2[parameters.size()]);
176 Resource r = g.getResource(uri);
177 service.addInstanceAdapter(
181 ? new ReflectionAdapter2<T>(clazz, parameterArray)
182 : new StaticMethodAdapter<T>(clazz, constructor, parameterArray));
186 } catch (Exception e) {
187 handleException(e, fileName);
191 private <T> void handleType(final Loader b, final Class<T> interface_, final Node node, String fileName) {
193 final NamedNodeMap attr = node.getAttributes();
194 final String uri = attr.getNamedItem(URI).getNodeValue();
195 Node constructorNode = attr.getNamedItem(CONSTRUCTOR);
196 final String constructor = constructorNode == null ? null : constructorNode.getNodeValue();
197 //System.out.println("AdapterRegistry2.handleType: " + b + " " + uri + " " + interface_);
199 new AdapterInstaller() {
202 public void install(ReadGraph g, AdaptionService service) throws Exception {
204 Class<? extends T> clazz =
205 ((Class<?>)b.loadClass(attr.getNamedItem(CLASS).getNodeValue()))
206 .asSubclass(interface_);
207 List<IDynamicAdapter2> parameters = readParameters(g, node, b);
208 IDynamicAdapter2[] parameterArray =
209 parameters.toArray(new IDynamicAdapter2[parameters.size()]);
214 ? new ReflectionAdapter2<T>(clazz, parameterArray)
215 : new StaticMethodAdapter<T>(clazz, constructor, parameterArray));
217 System.err.println("Failed to adapt "+interface_.getName());
219 } catch(RuntimeException t) {
220 System.err.println("Failed to adapt "+interface_.getName());
226 } catch (Exception e) {
227 handleException(e, fileName);
231 private List<IDynamicAdapter2> readParameters(ReadGraph g, Node node, Loader b) throws DatabaseException, DOMException, ClassNotFoundException {
232 NodeList nodeList = node.getChildNodes();
233 ArrayList<IDynamicAdapter2> parameters = new ArrayList<IDynamicAdapter2>();
234 for(int i=0;i<nodeList.getLength();++i) {
235 Node n = nodeList.item(i);
236 if(n.getNodeType() == Node.ELEMENT_NODE) {
237 NamedNodeMap attr = n.getAttributes();
238 IDynamicAdapter2 da = null;
239 if(n.getNodeName().equals("this"))
240 da = new ThisResource2();
241 else if(n.getNodeName().equals("graph"))
242 da = new GraphObject2();
243 else if(n.getNodeName().equals("bundle")) {
244 String bundleId = null;
245 Node fc = n.getFirstChild();
247 bundleId = fc.getNodeValue();
248 if (bundleId == null) {
249 da = new ConstantAdapter(Bundle.class, b.getBundle());
251 Bundle ob = Platform.getBundle(bundleId);
253 da = new ConstantAdapter(Bundle.class, ob);
255 throw new DOMException(DOMException.NOT_FOUND_ERR, "bundle '" + bundleId + "' not found");
258 } else if(n.getNodeName().equals("related"))
259 da = new RelatedResources2(
260 g.getResource(attr.getNamedItem("uri").getNodeValue()));
261 else if(n.getNodeName().equals("orderedSet"))
262 da = new OrderedSetResources2(
263 g.getResource(attr.getNamedItem("uri").getNodeValue()));
264 else if(n.getNodeName().equals("single"))
265 da = new SingleRelatedResource2(
266 g.getResource(attr.getNamedItem("uri").getNodeValue()));
267 else if(n.getNodeName().equals("atMostOne"))
268 da = new AtMostOneRelatedResource2(
269 g.getResource(attr.getNamedItem("uri").getNodeValue()));
270 else if(n.getNodeName().equals("string"))
271 da = new ConstantAdapter(String.class, n.getFirstChild().getNodeValue());
273 Node toNode = attr.getNamedItem("to");
275 String to = toNode.getNodeValue();
276 da = new AdaptingDynamicAdapter2(da, b.loadClass(to));
285 private <T> void handleAdapter(final Loader b, final Class<T> interface_, Node node, String fileName) {
287 NamedNodeMap attr = node.getAttributes();
288 final String uri = attr.getNamedItem(URI).getNodeValue();
289 final String clazz = attr.getNamedItem(ADAPTER_CLASS).getNodeValue();
291 Node contextNode = attr.getNamedItem(CONTEXT_CLASS);
292 final Class<?> contextClass = contextNode != null ? b.loadClass(contextNode.getNodeValue()) : Resource.class;
294 //System.out.println("AdapterRegistry2.handleAdapter: " + b + " " + uri + " " + interface_ + ", class=" + clazz);
296 new AdapterInstaller() {
298 @SuppressWarnings("unchecked")
300 public void install(ReadGraph g, AdaptionService service) throws Exception {
305 ((Class<?>)b.loadClass(clazz))
306 .asSubclass(Adapter.class).newInstance());
310 } catch (Exception e) {
311 handleException(e, fileName);
315 private <T> void handleBaseType(Loader b, final Class<T> interface_, Node node, String fileName) {
317 NamedNodeMap attr = node.getAttributes();
318 final String uri = attr.getNamedItem(URI).getNodeValue();
320 new AdapterInstaller() {
323 public void install(ReadGraph g, AdaptionService service) throws Exception {
324 service.declareAdapter(
330 } catch (Exception e) {
331 handleException(e, fileName);
335 public void updateAdaptionService(Session s, final AdaptionService service) throws DatabaseException {
336 s.syncRequest(new Read() {
338 public Object perform(ReadGraph g) {
339 for(AdapterInstaller t : installerSources.keySet()) {
341 t.install(g, service);
342 } catch (Exception e) {
343 AdapterRegistry2.this.handleException(e, t);
351 public void initialize(ClassLoader b, String schemaURL, File[] files) {
355 DocumentBuilderFactory factory =
356 DocumentBuilderFactory.newInstance();
358 if(schemaURL != null && validateAgainstSchema()) {
360 factory.setValidating(true);
361 factory.setAttribute(
362 "http://java.sun.com/xml/jaxp/properties/schemaLanguage",
363 "http://www.w3.org/2001/XMLSchema");
364 factory.setAttribute(
365 "http://java.sun.com/xml/jaxp/properties/schemaSource", schemaURL);
369 // TODO Listen bundles (install/uninstall)
370 if (exceptions.isEmpty())
371 for (final File f : files) {
372 // String fileName = new Path(b.getLocation()).append(file.getPath()).toString();
374 DocumentBuilder builder = factory.newDocumentBuilder();
375 builder.setErrorHandler(new ErrorHandler() {
378 public void error(SAXParseException exception)
379 throws SAXException {
380 // TODO Put this error somewhere
381 System.err.println("Parse error at " + f.getAbsolutePath() +
382 // + b.getSymbolicName() + "/adapters.xml" +
383 " line " + exception.getLineNumber() +
384 " column " + exception.getColumnNumber() + ":");
385 System.err.println(exception.getMessage());
389 public void fatalError(SAXParseException exception)
390 throws SAXException {
395 public void warning(SAXParseException exception)
396 throws SAXException {
401 //System.out.println("bundle=" + b.getSymbolicName());
402 Document doc = builder.parse(f);
403 handleAdaptersDocument(loader(b), doc, f.getAbsolutePath());
404 } catch (Exception e) {
405 handleException(e, f.getAbsolutePath());
409 } catch (Exception e) {
410 handleException(e, "(no file name available)");
415 private boolean validateAgainstSchema() {
416 return Platform.inDevelopmentMode();
419 public void initialize(BundleContext context) {
420 LOGGER.info("Initializing");
423 DocumentBuilderFactory factory =
424 DocumentBuilderFactory.newInstance();
426 if (validateAgainstSchema()) {
427 factory.setValidating(true);
428 factory.setAttribute(
429 "http://java.sun.com/xml/jaxp/properties/schemaLanguage",
430 "http://www.w3.org/2001/XMLSchema");
431 factory.setAttribute(
432 "http://java.sun.com/xml/jaxp/properties/schemaSource",
433 context.getBundle().getResource("adapters.xsd").toString());
436 // TODO Listen bundles (install/uninstall)
437 List<Future<?>> waitFor = new ArrayList<>();
438 if (exceptions.isEmpty())
439 for (final Bundle b : context.getBundles()) {
440 Future<?> submit = ThreadUtils.getNonBlockingWorkExecutor().submit(() -> {
441 URL file = b.getEntry(ADAPTERS_FILE);
443 String fileName = new Path(b.getLocation()).append(file.getPath()).toString();
445 DocumentBuilder builder = factory.newDocumentBuilder();
446 builder.setErrorHandler(new ErrorHandler() {
449 public void error(SAXParseException exception) throws SAXException {
450 // TODO Put this error somewhere
451 System.err.println("Parse error at " + b.getSymbolicName() + "/adapters.xml"
452 + " line " + exception.getLineNumber() + " column "
453 + exception.getColumnNumber() + ":");
454 System.err.println(exception.getMessage());
458 public void fatalError(SAXParseException exception) throws SAXException {
463 public void warning(SAXParseException exception) throws SAXException {
469 // System.out.println("bundle=" + b.getSymbolicName());
470 String text = FileUtils.getContents(file);
471 text = OntologyVersions.getInstance().currentVersion(text);
472 StringReader reader = new StringReader(text);
473 InputSource inputSource = new InputSource(reader);
474 Document doc = builder.parse(inputSource);
476 handleAdaptersDocument(loader(b), doc, fileName);
477 } catch (Exception e) {
478 handleException(e, fileName);
485 // Let's wait in here
486 waitFor.forEach(f -> {
489 } catch (InterruptedException | ExecutionException e) {
490 LOGGER.error("Could not wait adapters to load", e);
493 LOGGER.info("Adapters installed");
494 } catch (Exception e) {
495 handleException(e, "(no file name available)");
500 Class<?> loadClass(String name) throws ClassNotFoundException ;
504 private Loader loader(final Bundle b) {
505 return new Loader() {
508 public Class<?> loadClass(String name) throws ClassNotFoundException {
509 return b.loadClass(name);
513 public Bundle getBundle() {
520 private Loader loader(final ClassLoader b) {
521 return new Loader() {
524 public Class<?> loadClass(String name) throws ClassNotFoundException {
525 return b.loadClass(name);
529 public Bundle getBundle() {