d962ba090d9919cebc6b0f00a2bffefa5e06489e
[simantics/platform.git] / bundles / org.simantics.project / src / org / simantics / project / management / PlatformUtil.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.project.management;
13
14 import java.io.BufferedInputStream;
15 import java.io.Closeable;
16 import java.io.DataInput;
17 import java.io.DataInputStream;
18 import java.io.File;
19 import java.io.FileNotFoundException;
20 import java.io.FileOutputStream;
21 import java.io.FilenameFilter;
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.io.UnsupportedEncodingException;
25 import java.net.URL;
26 import java.net.URLDecoder;
27 import java.util.ArrayList;
28 import java.util.Collection;
29 import java.util.Enumeration;
30 import java.util.Map.Entry;
31 import java.util.jar.Attributes;
32 import java.util.jar.Manifest;
33
34 import org.eclipse.core.internal.runtime.PlatformActivator;
35 import org.eclipse.core.runtime.FileLocator;
36 import org.eclipse.core.runtime.Platform;
37 import org.eclipse.equinox.p2.metadata.IVersionedId;
38 import org.eclipse.equinox.p2.metadata.Version;
39 import org.eclipse.equinox.p2.metadata.VersionedId;
40 import org.osgi.framework.Bundle;
41 import org.simantics.databoard.adapter.AdaptException;
42 import org.simantics.databoard.binding.Binding;
43 import org.simantics.databoard.binding.mutable.Variant;
44 import org.simantics.databoard.container.DataContainer;
45 import org.simantics.databoard.container.DataContainers;
46 import org.simantics.databoard.serialization.SerializationException;
47 import org.simantics.db.common.utils.Logger;
48 import org.simantics.graph.compiler.CompilationResult;
49 import org.simantics.graph.compiler.GraphCompiler;
50 import org.simantics.graph.compiler.GraphCompilerPreferences;
51 import org.simantics.graph.compiler.ValidationMode;
52 import org.simantics.graph.representation.TransferableGraph1;
53 import org.simantics.ltk.FileSource;
54 import org.simantics.ltk.ISource;
55 import org.simantics.ltk.Problem;
56 import org.simantics.scl.reflection.OntologyVersions;
57
58 /**
59  * This class contains utilities for managing bundles in a active platform. 
60  *
61  */
62 @SuppressWarnings("restriction")
63 public class PlatformUtil {
64
65         
66         /**
67          * Get all bundles in the platform.
68          * 
69          * @return
70          */
71         public static Bundle[] getBundles() {
72                 return PlatformActivator.getContext().getBundles();
73         }
74                 
75         /**
76          * Get the manifest file of a bundle
77          * 
78          * @param bundle bundle
79          * @return manifest or <tt>null</tt> if doesn't not exist
80          * @throws IOException 
81          */
82         public static Manifest getManifest(Bundle bundle) throws IOException {
83                 URL url = bundle.getEntry("META-INF/MANIFEST.MF");
84                 if (url==null) return null;
85                 InputStream is = url.openStream();
86                 try {
87                         return new Manifest(is);                        
88                 } finally {
89                         is.close();
90                 }
91         }
92         
93         /**
94          * Get the manifest file of a bundle
95          * 
96          * @param bundle bundle
97          * @return manifest or <tt>null</tt> if doesn't not exist
98          * @throws IOException 
99          */
100         public static Manifest getSimanticsManifest(Bundle bundle) throws IOException {
101                 URL url = bundle.getEntry("META-INF/SIMANTICS.MF");
102                 if (url==null) return null;
103                 InputStream is = url.openStream();
104                 try {
105                         return new Manifest(is);                        
106                 } finally {
107                         is.close();
108                 }
109         }
110         
111         /**
112          * Get a list (BundleIds) of all user installable units. These are the 
113          * top-level items that are visible for the end-user. 
114          * The list is acquired from the bundles of the current application. 
115          * 
116          * @param list of simantics features URIs
117          * @throws IOException 
118          */
119         public static void getUserInstallableUnits(Collection<String> list) 
120         throws IOException 
121         {
122                 for (Bundle bundle : getBundles()) {
123                         Manifest manifest = getSimanticsManifest(bundle);
124                         if (manifest==null) continue;
125                         Attributes attributes = manifest.getMainAttributes();
126                         for (Entry<Object, Object> entry2 : attributes.entrySet()) {
127                                 Object key = entry2.getKey();
128                         if (key.toString().contains("Installable-Unit")) {
129                                 String bid = entry2.getValue().toString();                              
130                                 list.add( bid );
131                         }
132                         }
133                 }               
134         }
135         
136         /**
137          * Get all transferable graphs in the platform
138          * 
139          * @param result collection to be filled with transferable graph info 
140          */
141         public static void getPlatformTGInfos(Collection<TGInfo> result) {
142                 for (Bundle bundle : getBundles()) {
143                         Enumeration<URL> e = bundle.findEntries("graphs/", "*.tg", false);
144                         if (e==null) continue;
145                         while (e.hasMoreElements()) {
146                                 org.osgi.framework.Version osgiVersion = bundle.getVersion();
147                                 Version p2Version = Version.createOSGi(osgiVersion.getMajor(), osgiVersion.getMinor(), osgiVersion.getMicro(), osgiVersion.getQualifier());
148                                 String id = bundle.getSymbolicName();
149                                 
150                                 TGInfo info = new TGInfo();
151                                 info.location = e.nextElement();
152                                 info.bundle = bundle;
153                                 info.vid = new VersionedId(id, p2Version);
154                                 result.add( info );
155                         }
156                 }
157         }
158
159     private static void uncheckedClose(Closeable closeable) {
160         try {
161             if (closeable != null)
162                 closeable.close();
163         } catch (IOException e) {
164             //ignore
165         }
166     }
167         
168     private static File copyResource(URL url, File targetFile) throws IOException, FileNotFoundException {
169         FileOutputStream os = null;
170         InputStream is = null;
171         try {
172             if (targetFile.exists())
173                 targetFile.delete();
174
175             is = url.openStream();
176             int read;
177             byte [] buffer = new byte [16384];
178             os = new FileOutputStream (targetFile);
179             while ((read = is.read (buffer)) != -1) {
180                 os.write(buffer, 0, read);
181             }
182             os.close ();
183             is.close ();
184
185             return targetFile;
186         } finally {
187             uncheckedClose(os);
188             uncheckedClose(is);
189         }
190     }
191         
192     private static File extractLib(URL libURL, String libName) throws FileNotFoundException, IOException {
193         String tmpDirStr = System.getProperty("java.io.tmpdir");
194         if (tmpDirStr == null)
195             throw new NullPointerException("java.io.tmpdir property is null");
196         File tmpDir = new File(tmpDirStr);
197         File libFile = new File(tmpDir, libName);
198         return copyResource(libURL, libFile);
199     }
200         
201     private static File url2file(URL url, String fileName) {
202                 if ("file".equals(url.getProtocol())) {
203                         try {
204                                 File path = new File(URLDecoder.decode(url.getPath(), "UTF-8"));
205                                 return path;
206                         } catch (UnsupportedEncodingException e) {
207                                 Logger.defaultLogError(e);
208                         }
209                 } else if ("jar".equals(url.getProtocol())) {
210                         try {
211                                 File libFile = extractLib(url, fileName);
212                                 return libFile;
213                         } catch (FileNotFoundException e) {
214                                 Logger.defaultLogError(e);
215                         } catch (IOException e) {
216                                 Logger.defaultLogError(e);
217                         }
218                 } else {
219                         System.err.println("Unsupported URL protocol '" + url + "' for FastLZ native library file '" + fileName);
220                 }       
221                 return null;
222         }
223         
224         public static void compile(Bundle b) throws Exception {
225                 
226                 Collection<ISource> sources = new ArrayList<ISource>();
227                 Collection<TransferableGraph1> dependencies = new ArrayList<TransferableGraph1>();
228                 
229                 for (Bundle b2 : getBundles()) {
230                         if(b.equals(b2)) continue;
231                         URL url = b2.getEntry("graph.tg");
232                         if (url==null) continue;
233                         File graphFile = url2file(FileLocator.resolve(b2.getEntry("/graph.tg")), b2.toString());
234                         dependencies.add(GraphCompiler.read(graphFile));
235                 }
236                 
237                 File bundleFile = FileLocator.getBundleFile(b);
238                 if(bundleFile.isDirectory()) {
239                         File folder = new File(bundleFile, "dynamicGraph");
240                         for(File f : folder.listFiles(new FilenameFilter() {
241                                 
242                                 @Override
243                                 public boolean accept(File dir, String name) {
244                                         return name.endsWith(".pgraph");
245                                 }
246                                 
247                         })) {
248                                 sources.add(new FileSource(f));
249                         }
250                 }               
251                 
252 //              System.out.println("source is " + tmpFile.getAbsolutePath());
253                 
254                 final StringBuilder errorStringBuilder = new StringBuilder();
255                 GraphCompilerPreferences prefs = new GraphCompilerPreferences();
256                 prefs.validate = true;
257                 prefs.validateRelationRestrictions = ValidationMode.ERROR;
258                 prefs.validateResourceHasType = ValidationMode.ERROR;
259                 String currentLayer0Version = OntologyVersions.getInstance().currentOntologyVersion("http://www.simantics.org/Layer0-0.0");
260                 CompilationResult result = GraphCompiler.compile(currentLayer0Version, sources, dependencies, null, prefs);
261                 
262                 for(Problem problem : result.getErrors())
263                         errorStringBuilder.append(problem.getLocation() + ": " + problem.getDescription() + "\n");
264                 for(Problem problem : result.getWarnings())
265                         errorStringBuilder.append(problem.getLocation() + ": " + problem.getDescription() + "\n");
266
267                 if(errorStringBuilder.length() > 0) {
268                         Logger.defaultLogError(errorStringBuilder.toString());
269                 } else {
270                         DataContainers.writeFile(new File(bundleFile, "graph.tg"), 
271                                         new DataContainer("graph", 1, new Variant(TransferableGraph1.BINDING, result.getGraph())));
272                 }
273                 
274         }
275         
276         /**
277          * Compile all dynamic ontologies in the Platform
278          * 
279          * @param collection
280          * @throws IOException
281          */
282         public static void compileAllDynamicOntologies() {
283                 for (Bundle bundle : getBundles()) {
284                         if(bundle.getEntry("dynamicGraph") != null) {
285                                 try {
286                                         File bundleFile = FileLocator.getBundleFile(bundle);
287                                         if(bundleFile.isDirectory()) {
288                                                 File tg = new File(bundleFile, "graph.tg");
289                                                 long tgLastModified = tg.lastModified();
290                                                 File folder = new File(bundleFile, "dynamicGraph");
291                                                 for(File f : folder.listFiles()) {
292                                                         if(f.isFile() && f.getName().endsWith(".pgraph") && f.lastModified() > tgLastModified) {
293                                                                 compile(bundle);
294                                                                 break;
295                                                         }
296                                                 }
297                                         }
298                                 } catch (Throwable e) {
299                                         Logger.defaultLogError(e);
300                                 }
301                         }
302                 }
303         }
304         
305         /**
306          * Get all graphs in the Platform
307          * 
308          * @param collection
309          * @throws IOException
310          */
311         public static void getAllGraphs(Collection<GraphBundle> collection) throws IOException {
312                 for (Bundle bundle : getBundles()) {
313                         GraphBundle entry = getGraph(bundle);
314                         if (entry!=null) collection.add(entry);
315                 }
316         }
317
318         /**
319          * Get bundle 
320          * 
321          * @param symbolicName
322          * @return bundle or <tt>null</tt> if there is no bundle or graph 
323          * @throws IOException
324          */
325         public static GraphBundle getGraph(String symbolicName) throws IOException {
326                 Bundle bundle = Platform.getBundle(symbolicName);
327                 if (bundle == null) return null;
328                 return getGraph( bundle );
329         }
330         
331         /**
332          * Read the graph in a graph bundle. Graph is read from "graph.tg" file in the root.
333          * 
334          * @param bundle
335          * @return transferable graph, or <tt>null</tt> if there is no graph in the bundle. 
336          * @throws IOException 
337          */
338         public static GraphBundleEx getGraph(Bundle bundle) throws IOException {
339                 URL url = bundle.getEntry("graph.tg");
340                 
341                 if (url==null) return null;
342                 InputStream is = url.openStream(); 
343                 // NOTE: this is vital for performance.
344                 is = new BufferedInputStream(is, 128*1024);
345                 try {
346                         DataInput dis = new DataInputStream(is);
347                         // or
348                         // dis = new InputStreamReadable(is, <max limit>) to protect from OOM
349                         
350                         org.simantics.databoard.container.DataContainer container = 
351                                         DataContainers.readFile(dis); 
352
353                         Binding binding = TransferableGraph1.BINDING;
354                         TransferableGraph1 graph = (TransferableGraph1)container.content.getValue(binding);
355 //                      TransferableGraph1 graph = (TransferableGraph1) Files.readFile(is, binding);
356 //                      System.out.println("getGraph(" + bundle.getSymbolicName() + "): read transferable graph in " + (System.nanoTime()-start)*1e-6 + "ms");
357                         org.osgi.framework.Version osgiVersion = bundle.getVersion();
358                         Version p2Version = Version.createOSGi(osgiVersion.getMajor(), osgiVersion.getMinor(), osgiVersion.getMicro(), osgiVersion.getQualifier());
359                         String id = bundle.getSymbolicName();
360                         VersionedId vid = new VersionedId(id, p2Version);
361                         String name = (String) bundle.getHeaders().get("Bundle-Name");
362                         if (name == null) name = id;
363                         String immutable = (String) bundle.getHeaders().get("Immutable");
364                         boolean isImmutable = 
365                                         immutable != null ? 
366                                                         "true".equals(immutable) : 
367                                                                 true;
368
369 //                      System.out.println("getGraph(" + bundle.getSymbolicName() + "): before hashcode calculation in " + (System.nanoTime()-start)*1e-6 + "ms");
370                         GraphBundleEx entry = new GraphBundleEx(name, graph, vid, isImmutable);
371 //                      System.out.println("getGraph(" + bundle.getSymbolicName() + "): completed in " + (System.nanoTime()-start)*1e-6 + "ms");
372                         return entry;
373                 } catch (SerializationException e) {
374                         throw new IOException(e);
375                 } catch (IOException e) {
376                         throw new IOException("Problem loading graph.tg from bundle " + bundle.getSymbolicName(), e);
377                 } catch (RuntimeException e) {
378                         throw new IOException("Problem loading graph.tg from bundle " + bundle.getSymbolicName(), e);
379                 } catch (AdaptException e) {
380                         throw new IOException("Problem loading graph.tg from bundle " + bundle.getSymbolicName(), e);
381                 } finally {
382                         is.close();
383                 }
384         }
385         
386         public static class TGInfo {
387                 public Bundle bundle;
388                 public URL location;
389                 public IVersionedId vid;
390         }
391         
392 }
393