]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.db.services/src/org/simantics/db/services/adaption/AdapterRegistry2.java
WIP: Some old-ish adapter registry initialization improvements
[simantics/platform.git] / bundles / org.simantics.db.services / src / org / simantics / db / services / adaption / AdapterRegistry2.java
1 /*******************************************************************************
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management
3  * in Industry THTH ry.
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
8  *
9  * Contributors:
10  *     VTT Technical Research Centre of Finland - initial API and implementation
11  *******************************************************************************/
12 package org.simantics.db.services.adaption;
13
14 import java.io.File;
15 import java.io.StringReader;
16 import java.net.URL;
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;
23
24 import javax.xml.parsers.DocumentBuilder;
25 import javax.xml.parsers.DocumentBuilderFactory;
26
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;
64
65 public class AdapterRegistry2 {
66
67     private static final Logger LOGGER = LoggerFactory.getLogger(AdapterRegistry2.class);
68
69     public static final String ADAPTERS_FILE = "adapters.xml";
70
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";
84
85     static private AdapterRegistry2 instance = new AdapterRegistry2();
86     ConcurrentHashMap<AdapterInstaller, String> installerSources = new ConcurrentHashMap<>();
87     Collection<Exception> exceptions = new ArrayList<Exception>();
88
89     public static AdapterRegistry2 getInstance() {
90         return instance;
91     }
92
93     private void addInstaller(AdapterInstaller installer, String sourceDesc) {
94         installerSources.put(installer, sourceDesc);
95     }
96
97     private static void handleException(Exception e, String fileName) {
98         System.err.println("At " + fileName);
99         e.printStackTrace();
100     }
101
102     private void handleException(Exception e, AdapterInstaller installer) {
103         String desc = installerSources.get(installer);
104         if (desc != null)
105             System.err.println("At " + desc);
106         e.printStackTrace();
107     }
108
109     private void handleAdaptersDocument(Loader b, Document doc, String fileName) {
110         try {
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);
120                 }
121             }
122         } catch (Exception e) {
123             handleException(e, fileName);
124         }
125     }
126
127     private void handleTarget(Loader b, Node node, String fileName) {
128         try {
129             Class<?> interface_ =
130                 b.loadClass(node.getAttributes().getNamedItem("interface")
131                         .getNodeValue());
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);
144             }
145         } catch (Exception e) {
146             handleException(e, fileName);
147         }
148     }
149
150     private void handleInstaller(Loader b, Node node, String fileName) {
151         try {
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);
158         }
159     }
160
161     private <T> void handleResource(final Loader b, final Class<T> interface_, final Node node, String fileName) {
162         try {
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_);
169             addInstaller(
170
171                     new AdapterInstaller() {
172
173                         @Override
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(
181                                     r,
182                                     interface_,
183                                     constructor == null 
184                                     ? new ReflectionAdapter2<T>(clazz, parameterArray)
185                                     : new StaticMethodAdapter<T>(clazz, constructor, parameterArray));
186                         }
187
188                     }, fileName);
189         } catch (Exception e) {
190             handleException(e, fileName);
191         }
192     }
193
194     private <T> void handleType(final Loader b, final Class<T> interface_, final Node node, String fileName) {
195         try {
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_);
201             addInstaller(
202                     new AdapterInstaller() {
203
204                         @Override
205                         public void install(ReadGraph g, AdaptionService service) throws Exception {
206                                 try {
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()]);
213                             service.addAdapter(
214                                     g.getResource(uri),
215                                     interface_,
216                                     constructor == null 
217                                     ? new ReflectionAdapter2<T>(clazz, parameterArray)
218                                     : new StaticMethodAdapter<T>(clazz, constructor, parameterArray));
219                                 } catch(Error t) {
220                                         System.err.println("Failed to adapt "+interface_.getName());
221                                         throw t;
222                                 } catch(RuntimeException t) {
223                                         System.err.println("Failed to adapt "+interface_.getName());
224                                         throw t;
225                                 }
226                         }
227
228                     }, fileName);
229         } catch (Exception e) {
230             e.printStackTrace();
231             handleException(e, fileName);
232         }
233     }
234
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();
250                     if (fc != null)
251                         bundleId = fc.getNodeValue();
252                     if (bundleId == null) {
253                         da = new ConstantAdapter(Bundle.class, b.getBundle());
254                     } else {
255                         Bundle ob = Platform.getBundle(bundleId);
256                         if (ob != null) {
257                             da = new ConstantAdapter(Bundle.class, ob);
258                         } else {
259                             throw new DOMException(DOMException.NOT_FOUND_ERR, "bundle '" + bundleId + "' not found");
260                         }
261                     }
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());
276                 {
277                     Node toNode = attr.getNamedItem("to");
278                     if(toNode != null) {
279                         String to = toNode.getNodeValue();
280                         da = new AdaptingDynamicAdapter2(da, b.loadClass(to));
281                     }
282                 }
283                 parameters.add(da);
284             }
285         }
286         return parameters;
287     }
288
289     private <T> void handleAdapter(final Loader b, final Class<T> interface_, Node node, String fileName) {
290         try {
291             NamedNodeMap attr = node.getAttributes();
292             final String uri = attr.getNamedItem(URI).getNodeValue();
293             final String clazz = attr.getNamedItem(ADAPTER_CLASS).getNodeValue();
294
295             Node contextNode = attr.getNamedItem(CONTEXT_CLASS);
296             final Class<?> contextClass = contextNode != null ? b.loadClass(contextNode.getNodeValue()) : Resource.class;
297             
298             //System.out.println("AdapterRegistry2.handleAdapter: " + b + " " + uri + " " + interface_ + ", class=" + clazz);
299             addInstaller(
300                     new AdapterInstaller() {
301
302                         @SuppressWarnings("unchecked")
303                         @Override
304                         public void install(ReadGraph g, AdaptionService service) throws Exception {
305                             service.addAdapter(
306                                     g.getResource(uri),
307                                     interface_,
308                                     contextClass,
309                                     ((Class<?>)b.loadClass(clazz))
310                                     .asSubclass(Adapter.class).newInstance());
311                         }
312
313                     }, fileName);
314         } catch (Exception e) {
315             handleException(e, fileName);
316         }
317     }
318
319     private <T> void handleBaseType(Loader b, final Class<T> interface_, Node node, String fileName) {
320         try {
321             NamedNodeMap attr = node.getAttributes();
322             final String uri = attr.getNamedItem(URI).getNodeValue();
323             addInstaller(
324                     new AdapterInstaller() {
325
326                         @Override
327                         public void install(ReadGraph g, AdaptionService service) throws Exception {
328                             service.declareAdapter(
329                                     g.getResource(uri),
330                                     interface_);
331                         }
332
333                     }, fileName);
334         } catch (Exception e) {
335             handleException(e, fileName);
336         }
337     }
338
339     public void updateAdaptionService(Session s, final AdaptionService service) throws DatabaseException {
340         s.syncRequest(new ReadRequest() {
341             @Override
342             public void run(ReadGraph g) {
343                 for(AdapterInstaller t : installerSources.keySet()) {
344                     try {
345                         t.install(g, service);
346                     } catch (Exception e) {
347                         AdapterRegistry2.this.handleException(e, t);
348                     }
349                 }
350             }
351         });
352     }
353
354     public void initialize(ClassLoader b, String schemaURL, File[] files) {
355
356         try {
357                 
358             DocumentBuilderFactory factory =
359                 DocumentBuilderFactory.newInstance();
360             
361             if(schemaURL != null && validateAgainstSchema()) {
362             
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);
369                     
370             }
371
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();
376                         try {
377                             DocumentBuilder builder = factory.newDocumentBuilder();
378                             builder.setErrorHandler(new ErrorHandler() {
379
380                                 @Override
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());
389                                 }
390
391                                 @Override
392                                 public void fatalError(SAXParseException exception)
393                                 throws SAXException {
394                                     error(exception);
395                                 }
396
397                                 @Override
398                                 public void warning(SAXParseException exception)
399                                 throws SAXException {
400                                     error(exception);
401                                 }
402
403                             });
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());
409
410                         }
411                     }
412         } catch (Exception e) {
413             handleException(e, "(no file name available)");
414         }
415         
416     }
417
418     private boolean validateAgainstSchema() {
419         return Platform.inDevelopmentMode();
420     }
421
422         public void initialize(BundleContext context) {
423             LOGGER.info("Initializing");
424         try {
425                 
426             DocumentBuilderFactory factory =
427                 DocumentBuilderFactory.newInstance();
428
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());
437             }
438
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);
445                         if (file != null) {
446                             String fileName = new Path(b.getLocation()).append(file.getPath()).toString();
447                             try {
448                                 DocumentBuilder builder = factory.newDocumentBuilder();
449                                 builder.setErrorHandler(new ErrorHandler() {
450
451                                     @Override
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());
458                                     }
459
460                                     @Override
461                                     public void fatalError(SAXParseException exception) throws SAXException {
462                                         error(exception);
463                                     }
464
465                                     @Override
466                                     public void warning(SAXParseException exception) throws SAXException {
467                                         error(exception);
468                                     }
469
470                                 });
471
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);
478                                 reader.close();
479                                 handleAdaptersDocument(loader(b), doc, fileName);
480                             } catch (Exception e) {
481                                 handleException(e, fileName);
482
483                             }
484                         }
485                     });
486                     waitFor.add(submit);
487                 }
488             // Let's wait in here
489             waitFor.forEach(f -> {
490                 try {
491                     f.get();
492                 } catch (InterruptedException | ExecutionException e) {
493                     LOGGER.error("Could not wait adapters to load", e);
494                 }
495             });
496             LOGGER.info("Adapters installed");
497         } catch (Exception e) {
498             handleException(e, "(no file name available)");
499         }
500     }
501
502         interface Loader {
503                 Class<?> loadClass(String name) throws ClassNotFoundException ;
504                 Bundle getBundle();
505         }
506
507         private Loader loader(final Bundle b) {
508                 return new Loader() {
509
510                         @Override
511                         public Class<?> loadClass(String name) throws ClassNotFoundException {
512                                 return b.loadClass(name);
513                         }
514                         
515                         @Override
516                         public Bundle getBundle() {
517                                 return b;
518                         }
519                         
520                 };
521         }
522
523         private Loader loader(final ClassLoader b) {
524                 return new Loader() {
525
526                         @Override
527                         public Class<?> loadClass(String name) throws ClassNotFoundException {
528                                 return b.loadClass(name);
529                         }
530
531                         @Override
532                         public Bundle getBundle() {
533                                 return null;
534                         }
535                         
536                 };
537         }
538         
539 }