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