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