+/*******************************************************************************\r
+ * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
+ * in Industry THTH ry.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ * VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.project.management;\r
+\r
+import java.io.BufferedInputStream;\r
+import java.io.Closeable;\r
+import java.io.DataInput;\r
+import java.io.DataInputStream;\r
+import java.io.File;\r
+import java.io.FileNotFoundException;\r
+import java.io.FileOutputStream;\r
+import java.io.FilenameFilter;\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+import java.io.UnsupportedEncodingException;\r
+import java.net.URL;\r
+import java.net.URLDecoder;\r
+import java.util.ArrayList;\r
+import java.util.Collection;\r
+import java.util.Enumeration;\r
+import java.util.Map.Entry;\r
+import java.util.jar.Attributes;\r
+import java.util.jar.Manifest;\r
+\r
+import org.eclipse.core.internal.runtime.PlatformActivator;\r
+import org.eclipse.core.runtime.FileLocator;\r
+import org.eclipse.core.runtime.Platform;\r
+import org.eclipse.equinox.p2.metadata.IVersionedId;\r
+import org.eclipse.equinox.p2.metadata.Version;\r
+import org.eclipse.equinox.p2.metadata.VersionedId;\r
+import org.osgi.framework.Bundle;\r
+import org.simantics.databoard.adapter.AdaptException;\r
+import org.simantics.databoard.binding.Binding;\r
+import org.simantics.databoard.binding.mutable.Variant;\r
+import org.simantics.databoard.container.DataContainer;\r
+import org.simantics.databoard.container.DataContainers;\r
+import org.simantics.databoard.serialization.SerializationException;\r
+import org.simantics.db.common.utils.Logger;\r
+import org.simantics.graph.compiler.CompilationResult;\r
+import org.simantics.graph.compiler.GraphCompiler;\r
+import org.simantics.graph.compiler.GraphCompilerPreferences;\r
+import org.simantics.graph.compiler.ValidationMode;\r
+import org.simantics.graph.representation.TransferableGraph1;\r
+import org.simantics.ltk.FileSource;\r
+import org.simantics.ltk.ISource;\r
+import org.simantics.ltk.Problem;\r
+import org.simantics.scl.reflection.OntologyVersions;\r
+\r
+/**\r
+ * This class contains utilities for managing bundles in a active platform. \r
+ *\r
+ */\r
+@SuppressWarnings("restriction")\r
+public class PlatformUtil {\r
+\r
+ \r
+ /**\r
+ * Get all bundles in the platform.\r
+ * \r
+ * @return\r
+ */\r
+ public static Bundle[] getBundles() {\r
+ return PlatformActivator.getContext().getBundles();\r
+ }\r
+ \r
+ /**\r
+ * Get the manifest file of a bundle\r
+ * \r
+ * @param bundle bundle\r
+ * @return manifest or <tt>null</tt> if doesn't not exist\r
+ * @throws IOException \r
+ */\r
+ public static Manifest getManifest(Bundle bundle) throws IOException {\r
+ URL url = bundle.getEntry("META-INF/MANIFEST.MF");\r
+ if (url==null) return null;\r
+ InputStream is = url.openStream();\r
+ try {\r
+ return new Manifest(is); \r
+ } finally {\r
+ is.close();\r
+ }\r
+ }\r
+ \r
+ /**\r
+ * Get the manifest file of a bundle\r
+ * \r
+ * @param bundle bundle\r
+ * @return manifest or <tt>null</tt> if doesn't not exist\r
+ * @throws IOException \r
+ */\r
+ public static Manifest getSimanticsManifest(Bundle bundle) throws IOException {\r
+ URL url = bundle.getEntry("META-INF/SIMANTICS.MF");\r
+ if (url==null) return null;\r
+ InputStream is = url.openStream();\r
+ try {\r
+ return new Manifest(is); \r
+ } finally {\r
+ is.close();\r
+ }\r
+ }\r
+ \r
+ /**\r
+ * Get a list (BundleIds) of all user installable units. These are the \r
+ * top-level items that are visible for the end-user. \r
+ * The list is acquired from the bundles of the current application. \r
+ * \r
+ * @param list of simantics features URIs\r
+ * @throws IOException \r
+ */\r
+ public static void getUserInstallableUnits(Collection<String> list) \r
+ throws IOException \r
+ {\r
+ for (Bundle bundle : getBundles()) {\r
+ Manifest manifest = getSimanticsManifest(bundle);\r
+ if (manifest==null) continue;\r
+ Attributes attributes = manifest.getMainAttributes();\r
+ for (Entry<Object, Object> entry2 : attributes.entrySet()) {\r
+ Object key = entry2.getKey();\r
+ if (key.toString().contains("Installable-Unit")) {\r
+ String bid = entry2.getValue().toString(); \r
+ list.add( bid );\r
+ }\r
+ }\r
+ } \r
+ }\r
+ \r
+ /**\r
+ * Get all transferable graphs in the platform\r
+ * \r
+ * @param result collection to be filled with transferable graph info \r
+ */\r
+ public static void getPlatformTGInfos(Collection<TGInfo> result) {\r
+ for (Bundle bundle : getBundles()) {\r
+ Enumeration<URL> e = bundle.findEntries("graphs/", "*.tg", false);\r
+ if (e==null) continue;\r
+ while (e.hasMoreElements()) {\r
+ org.osgi.framework.Version osgiVersion = bundle.getVersion();\r
+ Version p2Version = Version.createOSGi(osgiVersion.getMajor(), osgiVersion.getMinor(), osgiVersion.getMicro(), osgiVersion.getQualifier());\r
+ String id = bundle.getSymbolicName();\r
+ \r
+ TGInfo info = new TGInfo();\r
+ info.location = e.nextElement();\r
+ info.bundle = bundle;\r
+ info.vid = new VersionedId(id, p2Version);\r
+ result.add( info );\r
+ }\r
+ }\r
+ }\r
+\r
+ private static void uncheckedClose(Closeable closeable) {\r
+ try {\r
+ if (closeable != null)\r
+ closeable.close();\r
+ } catch (IOException e) {\r
+ //ignore\r
+ }\r
+ }\r
+ \r
+ private static File copyResource(URL url, File targetFile) throws IOException, FileNotFoundException {\r
+ FileOutputStream os = null;\r
+ InputStream is = null;\r
+ try {\r
+ if (targetFile.exists())\r
+ targetFile.delete();\r
+\r
+ is = url.openStream();\r
+ int read;\r
+ byte [] buffer = new byte [16384];\r
+ os = new FileOutputStream (targetFile);\r
+ while ((read = is.read (buffer)) != -1) {\r
+ os.write(buffer, 0, read);\r
+ }\r
+ os.close ();\r
+ is.close ();\r
+\r
+ return targetFile;\r
+ } finally {\r
+ uncheckedClose(os);\r
+ uncheckedClose(is);\r
+ }\r
+ }\r
+ \r
+ private static File extractLib(URL libURL, String libName) throws FileNotFoundException, IOException {\r
+ String tmpDirStr = System.getProperty("java.io.tmpdir");\r
+ if (tmpDirStr == null)\r
+ throw new NullPointerException("java.io.tmpdir property is null");\r
+ File tmpDir = new File(tmpDirStr);\r
+ File libFile = new File(tmpDir, libName);\r
+ return copyResource(libURL, libFile);\r
+ }\r
+ \r
+ private static File url2file(URL url, String fileName) {\r
+ if ("file".equals(url.getProtocol())) {\r
+ try {\r
+ File path = new File(URLDecoder.decode(url.getPath(), "UTF-8"));\r
+ return path;\r
+ } catch (UnsupportedEncodingException e) {\r
+ Logger.defaultLogError(e);\r
+ }\r
+ } else if ("jar".equals(url.getProtocol())) {\r
+ try {\r
+ File libFile = extractLib(url, fileName);\r
+ return libFile;\r
+ } catch (FileNotFoundException e) {\r
+ Logger.defaultLogError(e);\r
+ } catch (IOException e) {\r
+ Logger.defaultLogError(e);\r
+ }\r
+ } else {\r
+ System.err.println("Unsupported URL protocol '" + url + "' for FastLZ native library file '" + fileName);\r
+ } \r
+ return null;\r
+ }\r
+ \r
+ public static void compile(Bundle b) throws Exception {\r
+ \r
+ Collection<ISource> sources = new ArrayList<ISource>();\r
+ Collection<TransferableGraph1> dependencies = new ArrayList<TransferableGraph1>();\r
+ \r
+ for (Bundle b2 : getBundles()) {\r
+ if(b.equals(b2)) continue;\r
+ URL url = b2.getEntry("graph.tg");\r
+ if (url==null) continue;\r
+ File graphFile = url2file(FileLocator.resolve(b2.getEntry("/graph.tg")), b2.toString());\r
+ dependencies.add(GraphCompiler.read(graphFile));\r
+ }\r
+ \r
+ File bundleFile = FileLocator.getBundleFile(b);\r
+ if(bundleFile.isDirectory()) {\r
+ File folder = new File(bundleFile, "dynamicGraph");\r
+ for(File f : folder.listFiles(new FilenameFilter() {\r
+ \r
+ @Override\r
+ public boolean accept(File dir, String name) {\r
+ return name.endsWith(".pgraph");\r
+ }\r
+ \r
+ })) {\r
+ sources.add(new FileSource(f));\r
+ }\r
+ } \r
+ \r
+// System.out.println("source is " + tmpFile.getAbsolutePath());\r
+ \r
+ final StringBuilder errorStringBuilder = new StringBuilder();\r
+ GraphCompilerPreferences prefs = new GraphCompilerPreferences();\r
+ prefs.validate = true;\r
+ prefs.validateRelationRestrictions = ValidationMode.ERROR;\r
+ prefs.validateResourceHasType = ValidationMode.ERROR;\r
+ String currentLayer0Version = OntologyVersions.getInstance().currentOntologyVersion("http://www.simantics.org/Layer0-0.0");\r
+ CompilationResult result = GraphCompiler.compile(currentLayer0Version, sources, dependencies, null, prefs);\r
+ \r
+ for(Problem problem : result.getErrors())\r
+ errorStringBuilder.append(problem.getLocation() + ": " + problem.getDescription() + "\n");\r
+ for(Problem problem : result.getWarnings())\r
+ errorStringBuilder.append(problem.getLocation() + ": " + problem.getDescription() + "\n");\r
+\r
+ if(errorStringBuilder.length() > 0) {\r
+ Logger.defaultLogError(errorStringBuilder.toString());\r
+ } else {\r
+ DataContainers.writeFile(new File(bundleFile, "graph.tg"), \r
+ new DataContainer("graph", 1, new Variant(TransferableGraph1.BINDING, result.getGraph())));\r
+ }\r
+ \r
+ }\r
+ \r
+ /**\r
+ * Compile all dynamic ontologies in the Platform\r
+ * \r
+ * @param collection\r
+ * @throws IOException\r
+ */\r
+ public static void compileAllDynamicOntologies() {\r
+ for (Bundle bundle : getBundles()) {\r
+ if(bundle.getEntry("dynamicGraph") != null) {\r
+ try {\r
+ File bundleFile = FileLocator.getBundleFile(bundle);\r
+ if(bundleFile.isDirectory()) {\r
+ File tg = new File(bundleFile, "graph.tg");\r
+ long tgLastModified = tg.lastModified();\r
+ File folder = new File(bundleFile, "dynamicGraph");\r
+ for(File f : folder.listFiles()) {\r
+ if(f.isFile() && f.getName().endsWith(".pgraph") && f.lastModified() > tgLastModified) {\r
+ compile(bundle);\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ } catch (Throwable e) {\r
+ Logger.defaultLogError(e);\r
+ }\r
+ }\r
+ }\r
+ }\r
+ \r
+ /**\r
+ * Get all graphs in the Platform\r
+ * \r
+ * @param collection\r
+ * @throws IOException\r
+ */\r
+ public static void getAllGraphs(Collection<GraphBundle> collection) throws IOException {\r
+ for (Bundle bundle : getBundles()) {\r
+ GraphBundle entry = getGraph(bundle);\r
+ if (entry!=null) collection.add(entry);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Get bundle \r
+ * \r
+ * @param symbolicName\r
+ * @return bundle or <tt>null</tt> if there is no bundle or graph \r
+ * @throws IOException\r
+ */\r
+ public static GraphBundle getGraph(String symbolicName) throws IOException {\r
+ Bundle bundle = Platform.getBundle(symbolicName);\r
+ if (bundle == null) return null;\r
+ return getGraph( bundle );\r
+ }\r
+ \r
+ /**\r
+ * Read the graph in a graph bundle. Graph is read from "graph.tg" file in the root.\r
+ * \r
+ * @param bundle\r
+ * @return transferable graph, or <tt>null</tt> if there is no graph in the bundle. \r
+ * @throws IOException \r
+ */\r
+ public static GraphBundleEx getGraph(Bundle bundle) throws IOException {\r
+ URL url = bundle.getEntry("graph.tg");\r
+ \r
+ if (url==null) return null;\r
+ InputStream is = url.openStream(); \r
+ // NOTE: this is vital for performance.\r
+ is = new BufferedInputStream(is, 128*1024);\r
+ try {\r
+ DataInput dis = new DataInputStream(is);\r
+ // or\r
+ // dis = new InputStreamReadable(is, <max limit>) to protect from OOM\r
+ \r
+ org.simantics.databoard.container.DataContainer container = \r
+ DataContainers.readFile(dis); \r
+\r
+ Binding binding = TransferableGraph1.BINDING;\r
+ TransferableGraph1 graph = (TransferableGraph1)container.content.getValue(binding);\r
+// TransferableGraph1 graph = (TransferableGraph1) Files.readFile(is, binding);\r
+// System.out.println("getGraph(" + bundle.getSymbolicName() + "): read transferable graph in " + (System.nanoTime()-start)*1e-6 + "ms");\r
+ org.osgi.framework.Version osgiVersion = bundle.getVersion();\r
+ Version p2Version = Version.createOSGi(osgiVersion.getMajor(), osgiVersion.getMinor(), osgiVersion.getMicro(), osgiVersion.getQualifier());\r
+ String id = bundle.getSymbolicName();\r
+ VersionedId vid = new VersionedId(id, p2Version);\r
+ String name = (String) bundle.getHeaders().get("Bundle-Name");\r
+ if (name == null) name = id;\r
+ String immutable = (String) bundle.getHeaders().get("Immutable");\r
+ boolean isImmutable = \r
+ immutable != null ? \r
+ "true".equals(immutable) : \r
+ true;\r
+\r
+// System.out.println("getGraph(" + bundle.getSymbolicName() + "): before hashcode calculation in " + (System.nanoTime()-start)*1e-6 + "ms");\r
+ GraphBundleEx entry = new GraphBundleEx(name, graph, vid, isImmutable);\r
+// System.out.println("getGraph(" + bundle.getSymbolicName() + "): completed in " + (System.nanoTime()-start)*1e-6 + "ms");\r
+ return entry;\r
+ } catch (SerializationException e) {\r
+ throw new IOException(e);\r
+ } catch (IOException e) {\r
+ throw new IOException("Problem loading graph.tg from bundle " + bundle.getSymbolicName(), e);\r
+ } catch (RuntimeException e) {\r
+ throw new IOException("Problem loading graph.tg from bundle " + bundle.getSymbolicName(), e);\r
+ } catch (AdaptException e) {\r
+ throw new IOException("Problem loading graph.tg from bundle " + bundle.getSymbolicName(), e);\r
+ } finally {\r
+ is.close();\r
+ }\r
+ }\r
+ \r
+ public static class TGInfo {\r
+ public Bundle bundle;\r
+ public URL location;\r
+ public IVersionedId vid;\r
+ }\r
+ \r
+}\r
+\r