]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.db.services/src/org/simantics/db/services/adaption/AdapterRegistry2.java
Start of AdapterRegistry2 internal query caching.
[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.HashMap;
20 import java.util.List;
21 import java.util.Map;
22
23 import javax.xml.parsers.DocumentBuilder;
24 import javax.xml.parsers.DocumentBuilderFactory;
25
26 import org.eclipse.core.runtime.Path;
27 import org.eclipse.core.runtime.Platform;
28 import org.osgi.framework.Bundle;
29 import org.osgi.framework.BundleContext;
30 import org.simantics.db.ReadGraph;
31 import org.simantics.db.Resource;
32 import org.simantics.db.Session;
33 import org.simantics.db.adaption.Adapter;
34 import org.simantics.db.adaption.AdapterInstaller;
35 import org.simantics.db.adaption.AdaptionService;
36 import org.simantics.db.common.request.ReadRequest;
37 import org.simantics.db.exception.DatabaseException;
38 import org.simantics.db.services.adaption.reflection.AdaptingDynamicAdapter2;
39 import org.simantics.db.services.adaption.reflection.AtMostOneRelatedResource2;
40 import org.simantics.db.services.adaption.reflection.ConstantAdapter;
41 import org.simantics.db.services.adaption.reflection.GraphObject2;
42 import org.simantics.db.services.adaption.reflection.IDynamicAdapter2;
43 import org.simantics.db.services.adaption.reflection.OrderedSetResources2;
44 import org.simantics.db.services.adaption.reflection.ReflectionAdapter2;
45 import org.simantics.db.services.adaption.reflection.RelatedResources2;
46 import org.simantics.db.services.adaption.reflection.SingleRelatedResource2;
47 import org.simantics.db.services.adaption.reflection.StaticMethodAdapter;
48 import org.simantics.db.services.adaption.reflection.ThisResource2;
49 import org.simantics.scl.reflection.OntologyVersions;
50 import org.simantics.utils.FileUtils;
51 import org.w3c.dom.DOMException;
52 import org.w3c.dom.Document;
53 import org.w3c.dom.NamedNodeMap;
54 import org.w3c.dom.Node;
55 import org.w3c.dom.NodeList;
56 import org.xml.sax.ErrorHandler;
57 import org.xml.sax.InputSource;
58 import org.xml.sax.SAXException;
59 import org.xml.sax.SAXParseException;
60
61 public class AdapterRegistry2 {
62
63     public static final String ADAPTERS_FILE = "adapters.xml";
64
65     public static final String ADAPTERS = "adapters";
66     public static final String ADAPTER = "adapter";
67     public static final String TARGET = "target";
68     public static final String BASE_TYPE = "baseType";
69     public static final String TYPE = "type";
70     public static final String RESOURCE = "resource";
71     public static final String URI = "uri";
72     public static final String INTERFACE = "interface";
73     public static final String CLASS = "class";
74     public static final String ADAPTER_CLASS = "adapterClass";
75     public static final String CONTEXT_CLASS = "contextClass";
76     public static final String INSTALLER = "installer";
77     public static final String CONSTRUCTOR = "constructor";
78
79     static private AdapterRegistry2 instance = new AdapterRegistry2();
80     Collection<AdapterInstaller> installers = new ArrayList<AdapterInstaller>();
81     Map<AdapterInstaller, String> installerSources = new HashMap<AdapterInstaller, String>();
82     Collection<Exception> exceptions = new ArrayList<Exception>();
83
84     public static AdapterRegistry2 getInstance() {
85         return instance;
86     }
87
88     private void addInstaller(AdapterInstaller installer, String sourceDesc) {
89         installers.add(installer);
90         installerSources.put(installer, sourceDesc);
91     }
92
93     private void handleException(Exception e, String fileName) {
94         System.err.println("At " + fileName);
95         e.printStackTrace();
96     }
97
98     private void handleException(Exception e, AdapterInstaller installer) {
99         String desc = installerSources.get(installer);
100         if (desc != null)
101             System.err.println("At " + desc);
102         e.printStackTrace();
103     }
104
105     private void handleAdaptersDocument(Loader b, Document doc, String fileName) {
106         try {
107             Node node = doc.getDocumentElement();
108             if(node.getNodeName().equals(ADAPTERS)) {
109                 NodeList nodeList = node.getChildNodes();
110                 for(int i=0;i<nodeList.getLength();++i) {
111                     Node n = nodeList.item(i);
112                     if(n.getNodeName().equals(TARGET))
113                         handleTarget(b, n, fileName);
114                     else if(n.getNodeName().equals(INSTALLER))
115                         handleInstaller(b, n, fileName);
116                 }
117             }
118         } catch (Exception e) {
119             handleException(e, fileName);
120         }
121     }
122
123     private void handleTarget(Loader b, Node node, String fileName) {
124         try {
125             Class<?> interface_ =
126                 b.loadClass(node.getAttributes().getNamedItem("interface")
127                         .getNodeValue());
128             NodeList nodeList = node.getChildNodes();
129             for(int i=0;i<nodeList.getLength();++i) {
130                 Node n = nodeList.item(i);
131                 String nodeName = n.getNodeName();
132                 if(nodeName.equals(BASE_TYPE))
133                     handleBaseType(b, interface_, n, fileName);
134                 else if(nodeName.equals(TYPE))
135                     handleType(b, interface_, n, fileName);
136                 else if(nodeName.equals(ADAPTER))
137                     handleAdapter(b, interface_, n, fileName);
138                 else if(nodeName.equals(RESOURCE))
139                     handleResource(b, interface_, n, fileName);
140             }
141         } catch (Exception e) {
142             handleException(e, fileName);
143         }
144     }
145
146     private void handleInstaller(Loader b, Node node, String fileName) {
147         try {
148             AdapterInstaller installer =
149                 ((Class<?>)b.loadClass(node.getAttributes().getNamedItem("class").getNodeValue()))
150                 .asSubclass(AdapterInstaller.class).newInstance();
151             addInstaller(installer, fileName);
152         } catch (Exception e) {
153             handleException(e, fileName);
154         }
155     }
156
157     private <T> void handleResource(final Loader b, final Class<T> interface_, final Node node, String fileName) {
158         try {
159             NamedNodeMap attr = node.getAttributes();
160             final String uri = attr.getNamedItem(URI).getNodeValue();
161             final String className = attr.getNamedItem(CLASS).getNodeValue();
162             Node constructorNode = attr.getNamedItem(CONSTRUCTOR);
163             final String constructor = constructorNode == null ? null : constructorNode.getNodeValue();
164 //            System.out.println("AdapterRegistry2.handleResource: " + b + " " + uri + " " + interface_);
165             addInstaller(
166
167                     new AdapterInstaller() {
168
169                         @Override
170                         public void install(ReadGraph g, AdaptionService service) throws Exception {
171                             Class<? extends T> clazz = b.loadClass(className).asSubclass(interface_);
172                             List<IDynamicAdapter2> parameters = readParameters(g, node, b);
173                             IDynamicAdapter2[] parameterArray = 
174                                 parameters.toArray(new IDynamicAdapter2[parameters.size()]);
175                             Resource r = g.getResource(uri);
176                             service.addInstanceAdapter(
177                                     r,
178                                     interface_,
179                                     constructor == null 
180                                     ? new ReflectionAdapter2<T>(clazz, parameterArray)
181                                     : new StaticMethodAdapter<T>(clazz, constructor, parameterArray));
182                         }
183
184                     }, fileName);
185         } catch (Exception e) {
186             handleException(e, fileName);
187         }
188     }
189
190     private <T> void handleType(final Loader b, final Class<T> interface_, final Node node, String fileName) {
191         try {
192             final NamedNodeMap attr = node.getAttributes();
193             final String uri = attr.getNamedItem(URI).getNodeValue();
194             Node constructorNode = attr.getNamedItem(CONSTRUCTOR);
195             final String constructor = constructorNode == null ? null : constructorNode.getNodeValue();
196             //System.out.println("AdapterRegistry2.handleType: " + b + " " + uri + " " + interface_);
197             addInstaller(
198                     new AdapterInstaller() {
199
200                         @Override
201                         public void install(ReadGraph g, AdaptionService service) throws Exception {
202                                 try {
203                             Class<? extends T> clazz =
204                                 ((Class<?>)b.loadClass(attr.getNamedItem(CLASS).getNodeValue()))
205                                 .asSubclass(interface_);
206                             List<IDynamicAdapter2> parameters = readParameters(g, node, b);
207                             IDynamicAdapter2[] parameterArray = 
208                                 parameters.toArray(new IDynamicAdapter2[parameters.size()]);
209                             service.addAdapter(
210                                     g.getResource(uri),
211                                     interface_,
212                                     constructor == null 
213                                     ? new ReflectionAdapter2<T>(clazz, parameterArray)
214                                     : new StaticMethodAdapter<T>(clazz, constructor, parameterArray));
215                                 } catch(Error t) {
216                                         System.err.println("Failed to adapt "+interface_.getName());
217                                         throw t;
218                                 } catch(RuntimeException t) {
219                                         System.err.println("Failed to adapt "+interface_.getName());
220                                         throw t;
221                                 }
222                         }
223
224                     }, fileName);
225         } catch (Exception e) {
226             e.printStackTrace();
227             handleException(e, fileName);
228         }
229     }
230
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 = ThisResource2.INSTANCE;
241                 else if(n.getNodeName().equals("graph"))
242                     da = GraphObject2.INSTANCE;
243                 else if(n.getNodeName().equals("bundle")) {
244                     String bundleId = null;
245                     Node fc = n.getFirstChild();
246                     if (fc != null)
247                         bundleId = fc.getNodeValue();
248                     if (bundleId == null) {
249                         da = new ConstantAdapter(Bundle.class, b.getBundle());
250                     } else {
251                         Bundle ob = Platform.getBundle(bundleId);
252                         if (ob != null) {
253                             da = new ConstantAdapter(Bundle.class, ob);
254                         } else {
255                             throw new DOMException(DOMException.NOT_FOUND_ERR, "bundle '" + bundleId + "' not found");
256                         }
257                     }
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());
272                 {
273                     Node toNode = attr.getNamedItem("to");
274                     if(toNode != null) {
275                         String to = toNode.getNodeValue();
276                         da = new AdaptingDynamicAdapter2(da, b.loadClass(to));
277                     }
278                 }
279                 parameters.add(da);
280             }
281         }
282         return parameters;
283     }
284
285     private <T> void handleAdapter(final Loader b, final Class<T> interface_, Node node, String fileName) {
286         try {
287             NamedNodeMap attr = node.getAttributes();
288             final String uri = attr.getNamedItem(URI).getNodeValue();
289             final String clazz = attr.getNamedItem(ADAPTER_CLASS).getNodeValue();
290
291             Node contextNode = attr.getNamedItem(CONTEXT_CLASS);
292             final Class<?> contextClass = contextNode != null ? b.loadClass(contextNode.getNodeValue()) : Resource.class;
293             
294             //System.out.println("AdapterRegistry2.handleAdapter: " + b + " " + uri + " " + interface_ + ", class=" + clazz);
295             addInstaller(
296                     new AdapterInstaller() {
297
298                         @SuppressWarnings("unchecked")
299                         @Override
300                         public void install(ReadGraph g, AdaptionService service) throws Exception {
301                             service.addAdapter(
302                                     g.getResource(uri),
303                                     interface_,
304                                     contextClass,
305                                     ((Class<?>)b.loadClass(clazz))
306                                     .asSubclass(Adapter.class).newInstance());
307                         }
308
309                     }, fileName);
310         } catch (Exception e) {
311             handleException(e, fileName);
312         }
313     }
314
315     private <T> void handleBaseType(Loader b, final Class<T> interface_, Node node, String fileName) {
316         try {
317             NamedNodeMap attr = node.getAttributes();
318             final String uri = attr.getNamedItem(URI).getNodeValue();
319             addInstaller(
320                     new AdapterInstaller() {
321
322                         @Override
323                         public void install(ReadGraph g, AdaptionService service) throws Exception {
324                             service.declareAdapter(
325                                     g.getResource(uri),
326                                     interface_);
327                         }
328
329                     }, fileName);
330         } catch (Exception e) {
331             handleException(e, fileName);
332         }
333     }
334
335     public void updateAdaptionService(Session s, final AdaptionService service) throws DatabaseException {
336         s.syncRequest(new ReadRequest() {
337             @Override
338             public void run(ReadGraph g) {
339                 for(AdapterInstaller t : installers) {
340                     try {
341                         t.install(g, service);
342                     } catch (Exception e) {
343                         AdapterRegistry2.this.handleException(e, t);
344                     }
345                 }
346             }
347         });
348     }
349
350     public void initialize(ClassLoader b, String schemaURL, File[] files) {
351
352         try {
353                 
354             DocumentBuilderFactory factory =
355                 DocumentBuilderFactory.newInstance();
356             
357             if(schemaURL != null && validateAgainstSchema()) {
358             
359                     factory.setValidating(true);
360                     factory.setAttribute(
361                             "http://java.sun.com/xml/jaxp/properties/schemaLanguage",
362                     "http://www.w3.org/2001/XMLSchema");
363                     factory.setAttribute(
364                             "http://java.sun.com/xml/jaxp/properties/schemaSource", schemaURL);
365                     
366             }
367
368             // TODO Listen bundles (install/uninstall)
369             if (exceptions.isEmpty())
370                 for (final File f : files) {
371 //                        String fileName = new Path(b.getLocation()).append(file.getPath()).toString();
372                         try {
373                             DocumentBuilder builder = factory.newDocumentBuilder();
374                             builder.setErrorHandler(new ErrorHandler() {
375
376                                 @Override
377                                 public void error(SAXParseException exception)
378                                 throws SAXException {
379                                     // TODO Put this error somewhere
380                                     System.err.println("Parse error at " + f.getAbsolutePath() + 
381 //                                            + b.getSymbolicName() + "/adapters.xml" +
382                                             " line " + exception.getLineNumber() +
383                                             " column " + exception.getColumnNumber() + ":");
384                                     System.err.println(exception.getMessage());
385                                 }
386
387                                 @Override
388                                 public void fatalError(SAXParseException exception)
389                                 throws SAXException {
390                                     error(exception);
391                                 }
392
393                                 @Override
394                                 public void warning(SAXParseException exception)
395                                 throws SAXException {
396                                     error(exception);
397                                 }
398
399                             });
400                             //System.out.println("bundle=" + b.getSymbolicName());
401                             Document doc = builder.parse(f);
402                             handleAdaptersDocument(loader(b), doc, f.getAbsolutePath());
403                         } catch (Exception e) {
404                             handleException(e, f.getAbsolutePath());
405
406                         }
407                     }
408         } catch (Exception e) {
409             handleException(e, "(no file name available)");
410         }
411         
412     }
413
414     private boolean validateAgainstSchema() {
415         return Platform.inDevelopmentMode();
416     }
417
418         public void initialize(BundleContext context) {
419         
420         try {
421                 
422             DocumentBuilderFactory factory =
423                 DocumentBuilderFactory.newInstance();
424
425             if (validateAgainstSchema()) {
426                 factory.setValidating(true);
427                 factory.setAttribute(
428                         "http://java.sun.com/xml/jaxp/properties/schemaLanguage",
429                         "http://www.w3.org/2001/XMLSchema");
430                 factory.setAttribute(
431                         "http://java.sun.com/xml/jaxp/properties/schemaSource",
432                         context.getBundle().getResource("adapters.xsd").toString());
433             }
434
435             // TODO Listen bundles (install/uninstall)
436             if (exceptions.isEmpty())
437                 for (final Bundle b : context.getBundles()) {
438                     URL file = b.getEntry(ADAPTERS_FILE);
439                     if (file != null) {
440                         String fileName = new Path(b.getLocation()).append(file.getPath()).toString();
441                         try {
442                             DocumentBuilder builder = factory.newDocumentBuilder();
443                             builder.setErrorHandler(new ErrorHandler() {
444
445                                 @Override
446                                 public void error(SAXParseException exception)
447                                 throws SAXException {
448                                     // TODO Put this error somewhere
449                                     System.err.println("Parse error at "
450                                             + b.getSymbolicName() + "/adapters.xml" +
451                                             " line " + exception.getLineNumber() +
452                                             " column " + exception.getColumnNumber() + ":");
453                                     System.err.println(exception.getMessage());
454                                 }
455
456                                 @Override
457                                 public void fatalError(SAXParseException exception)
458                                 throws SAXException {
459                                     error(exception);
460                                 }
461
462                                 @Override
463                                 public void warning(SAXParseException exception)
464                                 throws SAXException {
465                                     error(exception);
466                                 }
467
468                             });
469                             
470                             //System.out.println("bundle=" + b.getSymbolicName());
471                             String text = FileUtils.getContents(file);
472                             text = OntologyVersions.getInstance().currentVersion(text);
473                             StringReader reader = new StringReader( text );
474                             InputSource inputSource = new InputSource( reader );
475                             Document doc = builder.parse( inputSource );
476                             reader.close();                                     
477                             handleAdaptersDocument(loader(b), doc, fileName);
478                         } catch (Exception e) {
479                             handleException(e, fileName);
480
481                         }
482                     }
483                 }
484         } catch (Exception e) {
485             handleException(e, "(no file name available)");
486         }
487     }
488
489         interface Loader {
490                 Class<?> loadClass(String name) throws ClassNotFoundException ;
491                 Bundle getBundle();
492         }
493
494         private Loader loader(final Bundle b) {
495                 return new Loader() {
496
497                         @Override
498                         public Class<?> loadClass(String name) throws ClassNotFoundException {
499                                 return b.loadClass(name);
500                         }
501                         
502                         @Override
503                         public Bundle getBundle() {
504                                 return b;
505                         }
506                         
507                 };
508         }
509
510         private Loader loader(final ClassLoader b) {
511                 return new Loader() {
512
513                         @Override
514                         public Class<?> loadClass(String name) throws ClassNotFoundException {
515                                 return b.loadClass(name);
516                         }
517
518                         @Override
519                         public Bundle getBundle() {
520                                 return null;
521                         }
522                         
523                 };
524         }
525         
526 }