/******************************************************************************* * Copyright (c) 2007, 2010 Association for Decentralized Information Management * in Industry THTH ry. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * VTT Technical Research Centre of Finland - initial API and implementation *******************************************************************************/ package org.simantics.db.services.adaption; import java.io.File; import java.io.StringReader; import java.net.URL; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Platform; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.simantics.db.ReadGraph; import org.simantics.db.Resource; import org.simantics.db.Session; import org.simantics.db.adaption.Adapter; import org.simantics.db.adaption.AdapterInstaller; import org.simantics.db.adaption.AdaptionService; import org.simantics.db.common.request.ReadRequest; import org.simantics.db.exception.DatabaseException; import org.simantics.db.request.Read; import org.simantics.db.services.adaption.reflection.AdaptingDynamicAdapter2; import org.simantics.db.services.adaption.reflection.AtMostOneRelatedResource2; import org.simantics.db.services.adaption.reflection.ConstantAdapter; import org.simantics.db.services.adaption.reflection.GraphObject2; import org.simantics.db.services.adaption.reflection.IDynamicAdapter2; import org.simantics.db.services.adaption.reflection.OrderedSetResources2; import org.simantics.db.services.adaption.reflection.ReflectionAdapter2; import org.simantics.db.services.adaption.reflection.RelatedResources2; import org.simantics.db.services.adaption.reflection.SingleRelatedResource2; import org.simantics.db.services.adaption.reflection.StaticMethodAdapter; import org.simantics.db.services.adaption.reflection.ThisResource2; import org.simantics.scl.reflection.OntologyVersions; import org.simantics.utils.FileUtils; import org.simantics.utils.threads.ThreadUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.DOMException; import org.w3c.dom.Document; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.ErrorHandler; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; public class AdapterRegistry2 { private static final Logger LOGGER = LoggerFactory.getLogger(AdapterRegistry2.class); public static final String ADAPTERS_FILE = "adapters.xml"; public static final String ADAPTERS = "adapters"; public static final String ADAPTER = "adapter"; public static final String TARGET = "target"; public static final String BASE_TYPE = "baseType"; public static final String TYPE = "type"; public static final String RESOURCE = "resource"; public static final String URI = "uri"; public static final String INTERFACE = "interface"; public static final String CLASS = "class"; public static final String ADAPTER_CLASS = "adapterClass"; public static final String CONTEXT_CLASS = "contextClass"; public static final String INSTALLER = "installer"; public static final String CONSTRUCTOR = "constructor"; static private AdapterRegistry2 instance = new AdapterRegistry2(); ConcurrentHashMap installerSources = new ConcurrentHashMap<>(); Collection exceptions = new ArrayList(); public static AdapterRegistry2 getInstance() { return instance; } private void addInstaller(AdapterInstaller installer, String sourceDesc) { installerSources.put(installer, sourceDesc); } private static void handleException(Exception e, String fileName) { System.err.println("At " + fileName); e.printStackTrace(); } private void handleException(Exception e, AdapterInstaller installer) { String desc = installerSources.get(installer); if (desc != null) System.err.println("At " + desc); e.printStackTrace(); } private void handleAdaptersDocument(Loader b, Document doc, String fileName) { try { Node node = doc.getDocumentElement(); if(node.getNodeName().equals(ADAPTERS)) { NodeList nodeList = node.getChildNodes(); for(int i=0;i interface_ = b.loadClass(node.getAttributes().getNamedItem("interface") .getNodeValue()); NodeList nodeList = node.getChildNodes(); for(int i=0;i)b.loadClass(node.getAttributes().getNamedItem("class").getNodeValue())) .asSubclass(AdapterInstaller.class).newInstance(); addInstaller(installer, fileName); } catch (Exception e) { handleException(e, fileName); } } private void handleResource(final Loader b, final Class interface_, final Node node, String fileName) { try { NamedNodeMap attr = node.getAttributes(); final String uri = attr.getNamedItem(URI).getNodeValue(); final String className = attr.getNamedItem(CLASS).getNodeValue(); Node constructorNode = attr.getNamedItem(CONSTRUCTOR); final String constructor = constructorNode == null ? null : constructorNode.getNodeValue(); // System.out.println("AdapterRegistry2.handleResource: " + b + " " + uri + " " + interface_); addInstaller( new AdapterInstaller() { @Override public void install(ReadGraph g, AdaptionService service) throws Exception { Class clazz = b.loadClass(className).asSubclass(interface_); List parameters = readParameters(g, node, b); IDynamicAdapter2[] parameterArray = parameters.toArray(new IDynamicAdapter2[parameters.size()]); Resource r = g.getResource(uri); service.addInstanceAdapter( r, interface_, constructor == null ? new ReflectionAdapter2(clazz, parameterArray) : new StaticMethodAdapter(clazz, constructor, parameterArray)); } }, fileName); } catch (Exception e) { handleException(e, fileName); } } private void handleType(final Loader b, final Class interface_, final Node node, String fileName) { try { final NamedNodeMap attr = node.getAttributes(); final String uri = attr.getNamedItem(URI).getNodeValue(); Node constructorNode = attr.getNamedItem(CONSTRUCTOR); final String constructor = constructorNode == null ? null : constructorNode.getNodeValue(); //System.out.println("AdapterRegistry2.handleType: " + b + " " + uri + " " + interface_); addInstaller( new AdapterInstaller() { @Override public void install(ReadGraph g, AdaptionService service) throws Exception { try { Class clazz = ((Class)b.loadClass(attr.getNamedItem(CLASS).getNodeValue())) .asSubclass(interface_); List parameters = readParameters(g, node, b); IDynamicAdapter2[] parameterArray = parameters.toArray(new IDynamicAdapter2[parameters.size()]); service.addAdapter( g.getResource(uri), interface_, constructor == null ? new ReflectionAdapter2(clazz, parameterArray) : new StaticMethodAdapter(clazz, constructor, parameterArray)); } catch(Error t) { System.err.println("Failed to adapt "+interface_.getName()); throw t; } catch(RuntimeException t) { System.err.println("Failed to adapt "+interface_.getName()); throw t; } } }, fileName); } catch (Exception e) { e.printStackTrace(); handleException(e, fileName); } } private List readParameters(ReadGraph g, Node node, Loader b) throws DatabaseException, DOMException, ClassNotFoundException { NodeList nodeList = node.getChildNodes(); ArrayList parameters = new ArrayList(); for(int i=0;i void handleAdapter(final Loader b, final Class interface_, Node node, String fileName) { try { NamedNodeMap attr = node.getAttributes(); final String uri = attr.getNamedItem(URI).getNodeValue(); final String clazz = attr.getNamedItem(ADAPTER_CLASS).getNodeValue(); Node contextNode = attr.getNamedItem(CONTEXT_CLASS); final Class contextClass = contextNode != null ? b.loadClass(contextNode.getNodeValue()) : Resource.class; //System.out.println("AdapterRegistry2.handleAdapter: " + b + " " + uri + " " + interface_ + ", class=" + clazz); addInstaller( new AdapterInstaller() { @SuppressWarnings("unchecked") @Override public void install(ReadGraph g, AdaptionService service) throws Exception { service.addAdapter( g.getResource(uri), interface_, contextClass, ((Class)b.loadClass(clazz)) .asSubclass(Adapter.class).newInstance()); } }, fileName); } catch (Exception e) { handleException(e, fileName); } } private void handleBaseType(Loader b, final Class interface_, Node node, String fileName) { try { NamedNodeMap attr = node.getAttributes(); final String uri = attr.getNamedItem(URI).getNodeValue(); addInstaller( new AdapterInstaller() { @Override public void install(ReadGraph g, AdaptionService service) throws Exception { service.declareAdapter( g.getResource(uri), interface_); } }, fileName); } catch (Exception e) { handleException(e, fileName); } } public void updateAdaptionService(Session s, final AdaptionService service) throws DatabaseException { s.syncRequest(new Read() { @Override public Object perform(ReadGraph g) { for(AdapterInstaller t : installerSources.keySet()) { try { t.install(g, service); } catch (Exception e) { AdapterRegistry2.this.handleException(e, t); } } return null; } }); } public void initialize(ClassLoader b, String schemaURL, File[] files) { try { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); if(schemaURL != null && validateAgainstSchema()) { factory.setValidating(true); factory.setAttribute( "http://java.sun.com/xml/jaxp/properties/schemaLanguage", "http://www.w3.org/2001/XMLSchema"); factory.setAttribute( "http://java.sun.com/xml/jaxp/properties/schemaSource", schemaURL); } // TODO Listen bundles (install/uninstall) if (exceptions.isEmpty()) for (final File f : files) { // String fileName = new Path(b.getLocation()).append(file.getPath()).toString(); try { DocumentBuilder builder = factory.newDocumentBuilder(); builder.setErrorHandler(new ErrorHandler() { @Override public void error(SAXParseException exception) throws SAXException { // TODO Put this error somewhere System.err.println("Parse error at " + f.getAbsolutePath() + // + b.getSymbolicName() + "/adapters.xml" + " line " + exception.getLineNumber() + " column " + exception.getColumnNumber() + ":"); System.err.println(exception.getMessage()); } @Override public void fatalError(SAXParseException exception) throws SAXException { error(exception); } @Override public void warning(SAXParseException exception) throws SAXException { error(exception); } }); //System.out.println("bundle=" + b.getSymbolicName()); Document doc = builder.parse(f); handleAdaptersDocument(loader(b), doc, f.getAbsolutePath()); } catch (Exception e) { handleException(e, f.getAbsolutePath()); } } } catch (Exception e) { handleException(e, "(no file name available)"); } } private boolean validateAgainstSchema() { return Platform.inDevelopmentMode(); } public void initialize(BundleContext context) { LOGGER.info("Initializing"); try { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); if (validateAgainstSchema()) { factory.setValidating(true); factory.setAttribute( "http://java.sun.com/xml/jaxp/properties/schemaLanguage", "http://www.w3.org/2001/XMLSchema"); factory.setAttribute( "http://java.sun.com/xml/jaxp/properties/schemaSource", context.getBundle().getResource("adapters.xsd").toString()); } // TODO Listen bundles (install/uninstall) List> waitFor = new ArrayList<>(); if (exceptions.isEmpty()) for (final Bundle b : context.getBundles()) { Future submit = ThreadUtils.getNonBlockingWorkExecutor().submit(() -> { URL file = b.getEntry(ADAPTERS_FILE); if (file != null) { String fileName = new Path(b.getLocation()).append(file.getPath()).toString(); try { DocumentBuilder builder = factory.newDocumentBuilder(); builder.setErrorHandler(new ErrorHandler() { @Override public void error(SAXParseException exception) throws SAXException { // TODO Put this error somewhere System.err.println("Parse error at " + b.getSymbolicName() + "/adapters.xml" + " line " + exception.getLineNumber() + " column " + exception.getColumnNumber() + ":"); System.err.println(exception.getMessage()); } @Override public void fatalError(SAXParseException exception) throws SAXException { error(exception); } @Override public void warning(SAXParseException exception) throws SAXException { error(exception); } }); // System.out.println("bundle=" + b.getSymbolicName()); String text = FileUtils.getContents(file); text = OntologyVersions.getInstance().currentVersion(text); StringReader reader = new StringReader(text); InputSource inputSource = new InputSource(reader); Document doc = builder.parse(inputSource); reader.close(); handleAdaptersDocument(loader(b), doc, fileName); } catch (Exception e) { handleException(e, fileName); } } }); waitFor.add(submit); } // Let's wait in here waitFor.forEach(f -> { try { f.get(); } catch (InterruptedException | ExecutionException e) { LOGGER.error("Could not wait adapters to load", e); } }); LOGGER.info("Adapters installed"); } catch (Exception e) { handleException(e, "(no file name available)"); } } interface Loader { Class loadClass(String name) throws ClassNotFoundException ; Bundle getBundle(); } private Loader loader(final Bundle b) { return new Loader() { @Override public Class loadClass(String name) throws ClassNotFoundException { return b.loadClass(name); } @Override public Bundle getBundle() { return b; } }; } private Loader loader(final ClassLoader b) { return new Loader() { @Override public Class loadClass(String name) throws ClassNotFoundException { return b.loadClass(name); } @Override public Bundle getBundle() { return null; } }; } }