1 /*******************************************************************************
2 * Copyright (c) 2007, 2010 Association for Decentralized Information Management
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
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.project.management;
14 import java.io.BufferedInputStream;
15 import java.io.Closeable;
16 import java.io.DataInput;
17 import java.io.DataInputStream;
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;
26 import java.net.URLDecoder;
27 import java.util.ArrayList;
28 import java.util.Arrays;
29 import java.util.Collection;
30 import java.util.Enumeration;
31 import java.util.Map.Entry;
32 import java.util.Objects;
33 import java.util.concurrent.CompletableFuture;
34 import java.util.concurrent.ExecutionException;
35 import java.util.jar.Attributes;
36 import java.util.jar.Manifest;
37 import java.util.stream.Collectors;
39 import org.eclipse.core.internal.runtime.PlatformActivator;
40 import org.eclipse.core.runtime.FileLocator;
41 import org.eclipse.core.runtime.Platform;
42 import org.eclipse.equinox.p2.metadata.IVersionedId;
43 import org.eclipse.equinox.p2.metadata.Version;
44 import org.eclipse.equinox.p2.metadata.VersionedId;
45 import org.osgi.framework.Bundle;
46 import org.simantics.databoard.binding.Binding;
47 import org.simantics.databoard.binding.mutable.Variant;
48 import org.simantics.databoard.container.DataContainer;
49 import org.simantics.databoard.container.DataContainers;
50 import org.simantics.graph.compiler.CompilationResult;
51 import org.simantics.graph.compiler.GraphCompiler;
52 import org.simantics.graph.compiler.GraphCompilerPreferences;
53 import org.simantics.graph.compiler.ValidationMode;
54 import org.simantics.graph.representation.TransferableGraph1;
55 import org.simantics.ltk.FileSource;
56 import org.simantics.ltk.ISource;
57 import org.simantics.ltk.Problem;
58 import org.simantics.scl.reflection.OntologyVersions;
59 import org.slf4j.Logger;
60 import org.slf4j.LoggerFactory;
63 * This class contains utilities for managing bundles in a active platform.
66 @SuppressWarnings("restriction")
67 public class PlatformUtil {
69 private static final Logger LOGGER = LoggerFactory.getLogger(PlatformUtil.class);
72 * Get all bundles in the platform.
76 public static Bundle[] getBundles() {
77 return PlatformActivator.getContext().getBundles();
81 * Get the manifest file of a bundle
83 * @param bundle bundle
84 * @return manifest or <tt>null</tt> if doesn't not exist
87 public static Manifest getManifest(Bundle bundle) throws IOException {
88 URL url = bundle.getEntry("META-INF/MANIFEST.MF");
89 if (url==null) return null;
90 InputStream is = url.openStream();
92 return new Manifest(is);
99 * Get the manifest file of a bundle
101 * @param bundle bundle
102 * @return manifest or <tt>null</tt> if doesn't not exist
103 * @throws IOException
105 public static Manifest getSimanticsManifest(Bundle bundle) throws IOException {
106 URL url = bundle.getEntry("META-INF/SIMANTICS.MF");
107 if (url==null) return null;
108 InputStream is = url.openStream();
110 return new Manifest(is);
117 * Get a list (BundleIds) of all user installable units. These are the
118 * top-level items that are visible for the end-user.
119 * The list is acquired from the bundles of the current application.
121 * @param list of simantics features URIs
122 * @throws IOException
124 public static void getUserInstallableUnits(Collection<String> list)
127 for (Bundle bundle : getBundles()) {
128 Manifest manifest = getSimanticsManifest(bundle);
129 if (manifest==null) continue;
130 Attributes attributes = manifest.getMainAttributes();
131 for (Entry<Object, Object> entry2 : attributes.entrySet()) {
132 Object key = entry2.getKey();
133 if (key.toString().contains("Installable-Unit")) {
134 String bid = entry2.getValue().toString();
142 * Get all transferable graphs in the platform
144 * @param result collection to be filled with transferable graph info
146 public static void getPlatformTGInfos(Collection<TGInfo> result) {
147 for (Bundle bundle : getBundles()) {
148 Enumeration<URL> e = bundle.findEntries("graphs/", "*.tg", false);
149 if (e==null) continue;
150 while (e.hasMoreElements()) {
151 org.osgi.framework.Version osgiVersion = bundle.getVersion();
152 Version p2Version = Version.createOSGi(osgiVersion.getMajor(), osgiVersion.getMinor(), osgiVersion.getMicro(), osgiVersion.getQualifier());
153 String id = bundle.getSymbolicName();
155 TGInfo info = new TGInfo();
156 info.location = e.nextElement();
157 info.bundle = bundle;
158 info.vid = new VersionedId(id, p2Version);
164 private static void uncheckedClose(Closeable closeable) {
166 if (closeable != null)
168 } catch (IOException e) {
173 private static File copyResource(URL url, File targetFile) throws IOException, FileNotFoundException {
174 FileOutputStream os = null;
175 InputStream is = null;
177 if (targetFile.exists())
180 is = url.openStream();
182 byte [] buffer = new byte [16384];
183 os = new FileOutputStream (targetFile);
184 while ((read = is.read (buffer)) != -1) {
185 os.write(buffer, 0, read);
197 private static File extractLib(URL libURL, String libName) throws FileNotFoundException, IOException {
198 String tmpDirStr = System.getProperty("java.io.tmpdir");
199 if (tmpDirStr == null)
200 throw new NullPointerException("java.io.tmpdir property is null");
201 File tmpDir = new File(tmpDirStr);
202 File libFile = new File(tmpDir, libName);
203 return copyResource(libURL, libFile);
206 private static File url2file(URL url, String fileName) {
207 if ("file".equals(url.getProtocol())) {
209 File path = new File(URLDecoder.decode(url.getPath(), "UTF-8"));
211 } catch (UnsupportedEncodingException e) {
212 LOGGER.error("Failed to decode " + url, e);
214 } else if ("jar".equals(url.getProtocol())) {
216 File libFile = extractLib(url, fileName);
218 } catch (FileNotFoundException e) {
219 LOGGER.error("Extraction to " + fileName + " failed, url not found: " + url, e);
220 } catch (IOException e) {
221 LOGGER.error("Extraction to " + fileName + " failed from url " + url, e);
224 LOGGER.error("Unsupported URL protocol '" + url + "' for FastLZ native library file '" + fileName);
229 public static void compile(Bundle b) throws Exception {
231 Collection<ISource> sources = new ArrayList<ISource>();
232 Collection<TransferableGraph1> dependencies = new ArrayList<TransferableGraph1>();
234 for (Bundle b2 : getBundles()) {
235 if(b.equals(b2)) continue;
236 URL url = b2.getEntry("graph.tg");
237 if (url==null) continue;
238 File graphFile = url2file(FileLocator.resolve(b2.getEntry("/graph.tg")), b2.toString());
239 dependencies.add(GraphCompiler.read(graphFile));
242 File bundleFile = FileLocator.getBundleFile(b);
243 if(bundleFile.isDirectory()) {
244 File folder = new File(bundleFile, "dynamicGraph");
245 for(File f : folder.listFiles(new FilenameFilter() {
248 public boolean accept(File dir, String name) {
249 return name.endsWith(".pgraph");
253 sources.add(new FileSource(f));
257 // System.out.println("source is " + tmpFile.getAbsolutePath());
259 final StringBuilder errorStringBuilder = new StringBuilder();
260 GraphCompilerPreferences prefs = new GraphCompilerPreferences();
261 prefs.validate = true;
262 prefs.validateRelationRestrictions = ValidationMode.ERROR;
263 prefs.validateResourceHasType = ValidationMode.ERROR;
264 String currentLayer0Version = OntologyVersions.getInstance().currentOntologyVersion("http://www.simantics.org/Layer0-0.0");
265 CompilationResult result = GraphCompiler.compile(currentLayer0Version, sources, dependencies, null, prefs);
267 for(Problem problem : result.getErrors())
268 errorStringBuilder.append(problem.getLocation() + ": " + problem.getDescription() + "\n");
269 for(Problem problem : result.getWarnings())
270 errorStringBuilder.append(problem.getLocation() + ": " + problem.getDescription() + "\n");
272 if(errorStringBuilder.length() > 0) {
273 LOGGER.error(errorStringBuilder.toString());
275 DataContainers.writeFile(new File(bundleFile, "graph.tg"),
276 new DataContainer("graph", 1, new Variant(TransferableGraph1.BINDING, result.getGraph())));
282 * Compile all dynamic ontologies in the Platform
285 * @throws IOException
287 public static void compileAllDynamicOntologies() {
288 for (Bundle bundle : getBundles()) {
289 if(bundle.getEntry("dynamicGraph") != null) {
291 File bundleFile = FileLocator.getBundleFile(bundle);
292 if(bundleFile.isDirectory()) {
293 File tg = new File(bundleFile, "graph.tg");
294 long tgLastModified = tg.lastModified();
295 File folder = new File(bundleFile, "dynamicGraph");
296 for(File f : folder.listFiles()) {
297 if(f.isFile() && f.getName().endsWith(".pgraph") && f.lastModified() > tgLastModified) {
303 } catch (Throwable e) {
304 LOGGER.error("Failed to compile dynamic ontologies in bundle " + bundle.getSymbolicName(), e);
311 * Get all graphs in the Platform
314 * @throws IOException
316 public static Collection<GraphBundle> getAllGraphs() throws IOException {
317 CompletableFuture<Object> f = new CompletableFuture<>();
318 Bundle[] bundles = getBundles();
319 Collection<GraphBundle> gbundles = Arrays.stream(bundles).map(t -> { // this could be done in parallel in the future?
320 if (f.isCompletedExceptionally())
323 return PlatformUtil.getGraph(t);
324 } catch (IOException e) {
325 if (LOGGER.isDebugEnabled())
326 LOGGER.debug("Could not get graph {}", t, e);
327 f.completeExceptionally(e);
330 }).filter(Objects::nonNull).collect(Collectors.toList());
331 if (f.isCompletedExceptionally()) {
334 } catch (ExecutionException | InterruptedException e) {
335 throw (IOException) e.getCause();
344 * @param symbolicName
345 * @return bundle or <tt>null</tt> if there is no bundle or graph
346 * @throws IOException
348 public static GraphBundle getGraph(String symbolicName) throws IOException {
349 Bundle bundle = Platform.getBundle(symbolicName);
350 if (bundle == null) return null;
351 return getGraph( bundle );
355 * Read the graph in a graph bundle. Graph is read from "graph.tg" file in the root.
358 * @return transferable graph, or <tt>null</tt> if there is no graph in the bundle.
359 * @throws IOException
361 public static GraphBundleEx getGraph(Bundle bundle) throws IOException {
362 URL url = bundle.getEntry("graph.tg");
364 if (url==null) return null;
365 InputStream is = url.openStream();
366 // NOTE: this is vital for performance.
367 is = new BufferedInputStream(is, 128*1024);
369 DataInput dis = new DataInputStream(is);
371 // dis = new InputStreamReadable(is, <max limit>) to protect from OOM
373 org.simantics.databoard.container.DataContainer container =
374 DataContainers.readFile(dis);
376 Binding binding = TransferableGraph1.BINDING;
377 TransferableGraph1 graph = (TransferableGraph1)container.content.getValue(binding);
378 // TransferableGraph1 graph = (TransferableGraph1) Files.readFile(is, binding);
379 // System.out.println("getGraph(" + bundle.getSymbolicName() + "): read transferable graph in " + (System.nanoTime()-start)*1e-6 + "ms");
380 org.osgi.framework.Version osgiVersion = bundle.getVersion();
381 Version p2Version = Version.createOSGi(osgiVersion.getMajor(), osgiVersion.getMinor(), osgiVersion.getMicro(), osgiVersion.getQualifier());
382 String id = bundle.getSymbolicName();
383 VersionedId vid = new VersionedId(id, p2Version);
384 String name = (String) bundle.getHeaders().get("Bundle-Name");
385 if (name == null) name = id;
386 String immutable = (String) bundle.getHeaders().get("Immutable");
387 boolean isImmutable =
389 "true".equals(immutable) :
392 // System.out.println("getGraph(" + bundle.getSymbolicName() + "): before hashcode calculation in " + (System.nanoTime()-start)*1e-6 + "ms");
393 GraphBundleEx entry = new GraphBundleEx(name, graph, vid, isImmutable);
394 // System.out.println("getGraph(" + bundle.getSymbolicName() + "): completed in " + (System.nanoTime()-start)*1e-6 + "ms");
396 } catch (Exception e) {
397 throw new IOException("Problem loading graph.tg from bundle " + bundle.getSymbolicName(), e);
399 LOGGER.error("Serious problem loading graph.tg from bundle " + bundle.getSymbolicName(), e);
406 public static class TGInfo {
407 public Bundle bundle;
409 public IVersionedId vid;