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.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 System.err.println("At " + fileName);
102 private void handleException(Exception e, AdapterInstaller installer) {
103 String desc = installerSources.get(installer);
105 System.err.println("At " + desc);
109 private void handleAdaptersDocument(Loader b, Document doc, String fileName) {
111 Node node = doc.getDocumentElement();
112 if(node.getNodeName().equals(ADAPTERS)) {
113 NodeList nodeList = node.getChildNodes();
114 for(int i=0;i<nodeList.getLength();++i) {
115 Node n = nodeList.item(i);
116 if(n.getNodeName().equals(TARGET))
117 handleTarget(b, n, fileName);
118 else if(n.getNodeName().equals(INSTALLER))
119 handleInstaller(b, n, fileName);
122 } catch (Exception e) {
123 handleException(e, fileName);
127 private void handleTarget(Loader b, Node node, String fileName) {
129 Class<?> interface_ =
130 b.loadClass(node.getAttributes().getNamedItem("interface")
132 NodeList nodeList = node.getChildNodes();
133 for(int i=0;i<nodeList.getLength();++i) {
134 Node n = nodeList.item(i);
135 String nodeName = n.getNodeName();
136 if(nodeName.equals(BASE_TYPE))
137 handleBaseType(b, interface_, n, fileName);
138 else if(nodeName.equals(TYPE))
139 handleType(b, interface_, n, fileName);
140 else if(nodeName.equals(ADAPTER))
141 handleAdapter(b, interface_, n, fileName);
142 else if(nodeName.equals(RESOURCE))
143 handleResource(b, interface_, n, fileName);
145 } catch (Exception e) {
146 handleException(e, fileName);
150 private void handleInstaller(Loader b, Node node, String fileName) {
152 AdapterInstaller installer =
153 ((Class<?>)b.loadClass(node.getAttributes().getNamedItem("class").getNodeValue()))
154 .asSubclass(AdapterInstaller.class).newInstance();
155 addInstaller(installer, fileName);
156 } catch (Exception e) {
157 handleException(e, fileName);
161 private <T> void handleResource(final Loader b, final Class<T> interface_, final Node node, String fileName) {
163 NamedNodeMap attr = node.getAttributes();
164 final String uri = attr.getNamedItem(URI).getNodeValue();
165 final String className = attr.getNamedItem(CLASS).getNodeValue();
166 Node constructorNode = attr.getNamedItem(CONSTRUCTOR);
167 final String constructor = constructorNode == null ? null : constructorNode.getNodeValue();
168 // System.out.println("AdapterRegistry2.handleResource: " + b + " " + uri + " " + interface_);
171 new AdapterInstaller() {
174 public void install(ReadGraph g, AdaptionService service) throws Exception {
175 Class<? extends T> clazz = b.loadClass(className).asSubclass(interface_);
176 List<IDynamicAdapter2> parameters = readParameters(g, node, b);
177 IDynamicAdapter2[] parameterArray =
178 parameters.toArray(new IDynamicAdapter2[parameters.size()]);
179 Resource r = g.getResource(uri);
180 service.addInstanceAdapter(
184 ? new ReflectionAdapter2<T>(clazz, parameterArray)
185 : new StaticMethodAdapter<T>(clazz, constructor, parameterArray));
189 } catch (Exception e) {
190 handleException(e, fileName);
194 private <T> void handleType(final Loader b, final Class<T> interface_, final Node node, String fileName) {
196 final NamedNodeMap attr = node.getAttributes();
197 final String uri = attr.getNamedItem(URI).getNodeValue();
198 Node constructorNode = attr.getNamedItem(CONSTRUCTOR);
199 final String constructor = constructorNode == null ? null : constructorNode.getNodeValue();
200 //System.out.println("AdapterRegistry2.handleType: " + b + " " + uri + " " + interface_);
202 new AdapterInstaller() {
205 public void install(ReadGraph g, AdaptionService service) throws Exception {
207 Class<? extends T> clazz =
208 ((Class<?>)b.loadClass(attr.getNamedItem(CLASS).getNodeValue()))
209 .asSubclass(interface_);
210 List<IDynamicAdapter2> parameters = readParameters(g, node, b);
211 IDynamicAdapter2[] parameterArray =
212 parameters.toArray(new IDynamicAdapter2[parameters.size()]);
217 ? new ReflectionAdapter2<T>(clazz, parameterArray)
218 : new StaticMethodAdapter<T>(clazz, constructor, parameterArray));
220 System.err.println("Failed to adapt "+interface_.getName());
222 } catch(RuntimeException t) {
223 System.err.println("Failed to adapt "+interface_.getName());
229 } catch (Exception e) {
231 handleException(e, fileName);
235 private List<IDynamicAdapter2> readParameters(ReadGraph g, Node node, Loader b) throws DatabaseException, DOMException, ClassNotFoundException {
236 NodeList nodeList = node.getChildNodes();
237 ArrayList<IDynamicAdapter2> parameters = new ArrayList<IDynamicAdapter2>();
238 for(int i=0;i<nodeList.getLength();++i) {
239 Node n = nodeList.item(i);
240 if(n.getNodeType() == Node.ELEMENT_NODE) {
241 NamedNodeMap attr = n.getAttributes();
242 IDynamicAdapter2 da = null;
243 if(n.getNodeName().equals("this"))
244 da = new ThisResource2();
245 else if(n.getNodeName().equals("graph"))
246 da = new GraphObject2();
247 else if(n.getNodeName().equals("bundle")) {
248 String bundleId = null;
249 Node fc = n.getFirstChild();
251 bundleId = fc.getNodeValue();
252 if (bundleId == null) {
253 da = new ConstantAdapter(Bundle.class, b.getBundle());
255 Bundle ob = Platform.getBundle(bundleId);
257 da = new ConstantAdapter(Bundle.class, ob);
259 throw new DOMException(DOMException.NOT_FOUND_ERR, "bundle '" + bundleId + "' not found");
262 } else if(n.getNodeName().equals("related"))
263 da = new RelatedResources2(
264 g.getResource(attr.getNamedItem("uri").getNodeValue()));
265 else if(n.getNodeName().equals("orderedSet"))
266 da = new OrderedSetResources2(
267 g.getResource(attr.getNamedItem("uri").getNodeValue()));
268 else if(n.getNodeName().equals("single"))
269 da = new SingleRelatedResource2(
270 g.getResource(attr.getNamedItem("uri").getNodeValue()));
271 else if(n.getNodeName().equals("atMostOne"))
272 da = new AtMostOneRelatedResource2(
273 g.getResource(attr.getNamedItem("uri").getNodeValue()));
274 else if(n.getNodeName().equals("string"))
275 da = new ConstantAdapter(String.class, n.getFirstChild().getNodeValue());
277 Node toNode = attr.getNamedItem("to");
279 String to = toNode.getNodeValue();
280 da = new AdaptingDynamicAdapter2(da, b.loadClass(to));
289 private <T> void handleAdapter(final Loader b, final Class<T> interface_, Node node, String fileName) {
291 NamedNodeMap attr = node.getAttributes();
292 final String uri = attr.getNamedItem(URI).getNodeValue();
293 final String clazz = attr.getNamedItem(ADAPTER_CLASS).getNodeValue();
295 Node contextNode = attr.getNamedItem(CONTEXT_CLASS);
296 final Class<?> contextClass = contextNode != null ? b.loadClass(contextNode.getNodeValue()) : Resource.class;
298 //System.out.println("AdapterRegistry2.handleAdapter: " + b + " " + uri + " " + interface_ + ", class=" + clazz);
300 new AdapterInstaller() {
302 @SuppressWarnings("unchecked")
304 public void install(ReadGraph g, AdaptionService service) throws Exception {
309 ((Class<?>)b.loadClass(clazz))
310 .asSubclass(Adapter.class).newInstance());
314 } catch (Exception e) {
315 handleException(e, fileName);
319 private <T> void handleBaseType(Loader b, final Class<T> interface_, Node node, String fileName) {
321 NamedNodeMap attr = node.getAttributes();
322 final String uri = attr.getNamedItem(URI).getNodeValue();
324 new AdapterInstaller() {
327 public void install(ReadGraph g, AdaptionService service) throws Exception {
328 service.declareAdapter(
334 } catch (Exception e) {
335 handleException(e, fileName);
339 public void updateAdaptionService(Session s, final AdaptionService service) throws DatabaseException {
340 s.syncRequest(new ReadRequest() {
342 public void run(ReadGraph g) {
343 for(AdapterInstaller t : installerSources.keySet()) {
345 t.install(g, service);
346 } catch (Exception e) {
347 AdapterRegistry2.this.handleException(e, t);
354 public void initialize(ClassLoader b, String schemaURL, File[] files) {
358 DocumentBuilderFactory factory =
359 DocumentBuilderFactory.newInstance();
361 if(schemaURL != null && validateAgainstSchema()) {
363 factory.setValidating(true);
364 factory.setAttribute(
365 "http://java.sun.com/xml/jaxp/properties/schemaLanguage",
366 "http://www.w3.org/2001/XMLSchema");
367 factory.setAttribute(
368 "http://java.sun.com/xml/jaxp/properties/schemaSource", schemaURL);
372 // TODO Listen bundles (install/uninstall)
373 if (exceptions.isEmpty())
374 for (final File f : files) {
375 // String fileName = new Path(b.getLocation()).append(file.getPath()).toString();
377 DocumentBuilder builder = factory.newDocumentBuilder();
378 builder.setErrorHandler(new ErrorHandler() {
381 public void error(SAXParseException exception)
382 throws SAXException {
383 // TODO Put this error somewhere
384 System.err.println("Parse error at " + f.getAbsolutePath() +
385 // + b.getSymbolicName() + "/adapters.xml" +
386 " line " + exception.getLineNumber() +
387 " column " + exception.getColumnNumber() + ":");
388 System.err.println(exception.getMessage());
392 public void fatalError(SAXParseException exception)
393 throws SAXException {
398 public void warning(SAXParseException exception)
399 throws SAXException {
404 //System.out.println("bundle=" + b.getSymbolicName());
405 Document doc = builder.parse(f);
406 handleAdaptersDocument(loader(b), doc, f.getAbsolutePath());
407 } catch (Exception e) {
408 handleException(e, f.getAbsolutePath());
412 } catch (Exception e) {
413 handleException(e, "(no file name available)");
418 private boolean validateAgainstSchema() {
419 return Platform.inDevelopmentMode();
422 public void initialize(BundleContext context) {
423 LOGGER.info("Initializing");
426 DocumentBuilderFactory factory =
427 DocumentBuilderFactory.newInstance();
429 if (validateAgainstSchema()) {
430 factory.setValidating(true);
431 factory.setAttribute(
432 "http://java.sun.com/xml/jaxp/properties/schemaLanguage",
433 "http://www.w3.org/2001/XMLSchema");
434 factory.setAttribute(
435 "http://java.sun.com/xml/jaxp/properties/schemaSource",
436 context.getBundle().getResource("adapters.xsd").toString());
439 // TODO Listen bundles (install/uninstall)
440 List<Future<?>> waitFor = new ArrayList<>();
441 if (exceptions.isEmpty())
442 for (final Bundle b : context.getBundles()) {
443 Future<?> submit = ThreadUtils.getNonBlockingWorkExecutor().submit(() -> {
444 URL file = b.getEntry(ADAPTERS_FILE);
446 String fileName = new Path(b.getLocation()).append(file.getPath()).toString();
448 DocumentBuilder builder = factory.newDocumentBuilder();
449 builder.setErrorHandler(new ErrorHandler() {
452 public void error(SAXParseException exception) throws SAXException {
453 // TODO Put this error somewhere
454 System.err.println("Parse error at " + b.getSymbolicName() + "/adapters.xml"
455 + " line " + exception.getLineNumber() + " column "
456 + exception.getColumnNumber() + ":");
457 System.err.println(exception.getMessage());
461 public void fatalError(SAXParseException exception) throws SAXException {
466 public void warning(SAXParseException exception) throws SAXException {
472 // System.out.println("bundle=" + b.getSymbolicName());
473 String text = FileUtils.getContents(file);
474 text = OntologyVersions.getInstance().currentVersion(text);
475 StringReader reader = new StringReader(text);
476 InputSource inputSource = new InputSource(reader);
477 Document doc = builder.parse(inputSource);
479 handleAdaptersDocument(loader(b), doc, fileName);
480 } catch (Exception e) {
481 handleException(e, fileName);
488 // Let's wait in here
489 waitFor.forEach(f -> {
492 } catch (InterruptedException | ExecutionException e) {
493 LOGGER.error("Could not wait adapters to load", e);
496 LOGGER.info("Adapters installed");
497 } catch (Exception e) {
498 handleException(e, "(no file name available)");
503 Class<?> loadClass(String name) throws ClassNotFoundException ;
507 private Loader loader(final Bundle b) {
508 return new Loader() {
511 public Class<?> loadClass(String name) throws ClassNotFoundException {
512 return b.loadClass(name);
516 public Bundle getBundle() {
523 private Loader loader(final ClassLoader b) {
524 return new Loader() {
527 public Class<?> loadClass(String name) throws ClassNotFoundException {
528 return b.loadClass(name);
532 public Bundle getBundle() {