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
10 * VTT Technical Research Centre of Finland - initial API and implementation
\r
11 *******************************************************************************/
\r
12 package org.simantics.project.management;
\r
14 import java.io.BufferedInputStream;
\r
15 import java.io.Closeable;
\r
16 import java.io.DataInput;
\r
17 import java.io.DataInputStream;
\r
18 import java.io.File;
\r
19 import java.io.FileNotFoundException;
\r
20 import java.io.FileOutputStream;
\r
21 import java.io.FilenameFilter;
\r
22 import java.io.IOException;
\r
23 import java.io.InputStream;
\r
24 import java.io.UnsupportedEncodingException;
\r
25 import java.net.URL;
\r
26 import java.net.URLDecoder;
\r
27 import java.util.ArrayList;
\r
28 import java.util.Collection;
\r
29 import java.util.Enumeration;
\r
30 import java.util.Map.Entry;
\r
31 import java.util.jar.Attributes;
\r
32 import java.util.jar.Manifest;
\r
34 import org.eclipse.core.internal.runtime.PlatformActivator;
\r
35 import org.eclipse.core.runtime.FileLocator;
\r
36 import org.eclipse.core.runtime.Platform;
\r
37 import org.eclipse.equinox.p2.metadata.IVersionedId;
\r
38 import org.eclipse.equinox.p2.metadata.Version;
\r
39 import org.eclipse.equinox.p2.metadata.VersionedId;
\r
40 import org.osgi.framework.Bundle;
\r
41 import org.simantics.databoard.adapter.AdaptException;
\r
42 import org.simantics.databoard.binding.Binding;
\r
43 import org.simantics.databoard.binding.mutable.Variant;
\r
44 import org.simantics.databoard.container.DataContainer;
\r
45 import org.simantics.databoard.container.DataContainers;
\r
46 import org.simantics.databoard.serialization.SerializationException;
\r
47 import org.simantics.db.common.utils.Logger;
\r
48 import org.simantics.graph.compiler.CompilationResult;
\r
49 import org.simantics.graph.compiler.GraphCompiler;
\r
50 import org.simantics.graph.compiler.GraphCompilerPreferences;
\r
51 import org.simantics.graph.compiler.ValidationMode;
\r
52 import org.simantics.graph.representation.TransferableGraph1;
\r
53 import org.simantics.ltk.FileSource;
\r
54 import org.simantics.ltk.ISource;
\r
55 import org.simantics.ltk.Problem;
\r
56 import org.simantics.scl.reflection.OntologyVersions;
\r
59 * This class contains utilities for managing bundles in a active platform.
\r
62 @SuppressWarnings("restriction")
\r
63 public class PlatformUtil {
\r
67 * Get all bundles in the platform.
\r
71 public static Bundle[] getBundles() {
\r
72 return PlatformActivator.getContext().getBundles();
\r
76 * Get the manifest file of a bundle
\r
78 * @param bundle bundle
\r
79 * @return manifest or <tt>null</tt> if doesn't not exist
\r
80 * @throws IOException
\r
82 public static Manifest getManifest(Bundle bundle) throws IOException {
\r
83 URL url = bundle.getEntry("META-INF/MANIFEST.MF");
\r
84 if (url==null) return null;
\r
85 InputStream is = url.openStream();
\r
87 return new Manifest(is);
\r
94 * Get the manifest file of a bundle
\r
96 * @param bundle bundle
\r
97 * @return manifest or <tt>null</tt> if doesn't not exist
\r
98 * @throws IOException
\r
100 public static Manifest getSimanticsManifest(Bundle bundle) throws IOException {
\r
101 URL url = bundle.getEntry("META-INF/SIMANTICS.MF");
\r
102 if (url==null) return null;
\r
103 InputStream is = url.openStream();
\r
105 return new Manifest(is);
\r
112 * Get a list (BundleIds) of all user installable units. These are the
\r
113 * top-level items that are visible for the end-user.
\r
114 * The list is acquired from the bundles of the current application.
\r
116 * @param list of simantics features URIs
\r
117 * @throws IOException
\r
119 public static void getUserInstallableUnits(Collection<String> list)
\r
120 throws IOException
\r
122 for (Bundle bundle : getBundles()) {
\r
123 Manifest manifest = getSimanticsManifest(bundle);
\r
124 if (manifest==null) continue;
\r
125 Attributes attributes = manifest.getMainAttributes();
\r
126 for (Entry<Object, Object> entry2 : attributes.entrySet()) {
\r
127 Object key = entry2.getKey();
\r
128 if (key.toString().contains("Installable-Unit")) {
\r
129 String bid = entry2.getValue().toString();
\r
137 * Get all transferable graphs in the platform
\r
139 * @param result collection to be filled with transferable graph info
\r
141 public static void getPlatformTGInfos(Collection<TGInfo> result) {
\r
142 for (Bundle bundle : getBundles()) {
\r
143 Enumeration<URL> e = bundle.findEntries("graphs/", "*.tg", false);
\r
144 if (e==null) continue;
\r
145 while (e.hasMoreElements()) {
\r
146 org.osgi.framework.Version osgiVersion = bundle.getVersion();
\r
147 Version p2Version = Version.createOSGi(osgiVersion.getMajor(), osgiVersion.getMinor(), osgiVersion.getMicro(), osgiVersion.getQualifier());
\r
148 String id = bundle.getSymbolicName();
\r
150 TGInfo info = new TGInfo();
\r
151 info.location = e.nextElement();
\r
152 info.bundle = bundle;
\r
153 info.vid = new VersionedId(id, p2Version);
\r
154 result.add( info );
\r
159 private static void uncheckedClose(Closeable closeable) {
\r
161 if (closeable != null)
\r
163 } catch (IOException e) {
\r
168 private static File copyResource(URL url, File targetFile) throws IOException, FileNotFoundException {
\r
169 FileOutputStream os = null;
\r
170 InputStream is = null;
\r
172 if (targetFile.exists())
\r
173 targetFile.delete();
\r
175 is = url.openStream();
\r
177 byte [] buffer = new byte [16384];
\r
178 os = new FileOutputStream (targetFile);
\r
179 while ((read = is.read (buffer)) != -1) {
\r
180 os.write(buffer, 0, read);
\r
187 uncheckedClose(os);
\r
188 uncheckedClose(is);
\r
192 private static File extractLib(URL libURL, String libName) throws FileNotFoundException, IOException {
\r
193 String tmpDirStr = System.getProperty("java.io.tmpdir");
\r
194 if (tmpDirStr == null)
\r
195 throw new NullPointerException("java.io.tmpdir property is null");
\r
196 File tmpDir = new File(tmpDirStr);
\r
197 File libFile = new File(tmpDir, libName);
\r
198 return copyResource(libURL, libFile);
\r
201 private static File url2file(URL url, String fileName) {
\r
202 if ("file".equals(url.getProtocol())) {
\r
204 File path = new File(URLDecoder.decode(url.getPath(), "UTF-8"));
\r
206 } catch (UnsupportedEncodingException e) {
\r
207 Logger.defaultLogError(e);
\r
209 } else if ("jar".equals(url.getProtocol())) {
\r
211 File libFile = extractLib(url, fileName);
\r
213 } catch (FileNotFoundException e) {
\r
214 Logger.defaultLogError(e);
\r
215 } catch (IOException e) {
\r
216 Logger.defaultLogError(e);
\r
219 System.err.println("Unsupported URL protocol '" + url + "' for FastLZ native library file '" + fileName);
\r
224 public static void compile(Bundle b) throws Exception {
\r
226 Collection<ISource> sources = new ArrayList<ISource>();
\r
227 Collection<TransferableGraph1> dependencies = new ArrayList<TransferableGraph1>();
\r
229 for (Bundle b2 : getBundles()) {
\r
230 if(b.equals(b2)) continue;
\r
231 URL url = b2.getEntry("graph.tg");
\r
232 if (url==null) continue;
\r
233 File graphFile = url2file(FileLocator.resolve(b2.getEntry("/graph.tg")), b2.toString());
\r
234 dependencies.add(GraphCompiler.read(graphFile));
\r
237 File bundleFile = FileLocator.getBundleFile(b);
\r
238 if(bundleFile.isDirectory()) {
\r
239 File folder = new File(bundleFile, "dynamicGraph");
\r
240 for(File f : folder.listFiles(new FilenameFilter() {
\r
243 public boolean accept(File dir, String name) {
\r
244 return name.endsWith(".pgraph");
\r
248 sources.add(new FileSource(f));
\r
252 // System.out.println("source is " + tmpFile.getAbsolutePath());
\r
254 final StringBuilder errorStringBuilder = new StringBuilder();
\r
255 GraphCompilerPreferences prefs = new GraphCompilerPreferences();
\r
256 prefs.validate = true;
\r
257 prefs.validateRelationRestrictions = ValidationMode.ERROR;
\r
258 prefs.validateResourceHasType = ValidationMode.ERROR;
\r
259 String currentLayer0Version = OntologyVersions.getInstance().currentOntologyVersion("http://www.simantics.org/Layer0-0.0");
\r
260 CompilationResult result = GraphCompiler.compile(currentLayer0Version, sources, dependencies, null, prefs);
\r
262 for(Problem problem : result.getErrors())
\r
263 errorStringBuilder.append(problem.getLocation() + ": " + problem.getDescription() + "\n");
\r
264 for(Problem problem : result.getWarnings())
\r
265 errorStringBuilder.append(problem.getLocation() + ": " + problem.getDescription() + "\n");
\r
267 if(errorStringBuilder.length() > 0) {
\r
268 Logger.defaultLogError(errorStringBuilder.toString());
\r
270 DataContainers.writeFile(new File(bundleFile, "graph.tg"),
\r
271 new DataContainer("graph", 1, new Variant(TransferableGraph1.BINDING, result.getGraph())));
\r
277 * Compile all dynamic ontologies in the Platform
\r
279 * @param collection
\r
280 * @throws IOException
\r
282 public static void compileAllDynamicOntologies() {
\r
283 for (Bundle bundle : getBundles()) {
\r
284 if(bundle.getEntry("dynamicGraph") != null) {
\r
286 File bundleFile = FileLocator.getBundleFile(bundle);
\r
287 if(bundleFile.isDirectory()) {
\r
288 File tg = new File(bundleFile, "graph.tg");
\r
289 long tgLastModified = tg.lastModified();
\r
290 File folder = new File(bundleFile, "dynamicGraph");
\r
291 for(File f : folder.listFiles()) {
\r
292 if(f.isFile() && f.getName().endsWith(".pgraph") && f.lastModified() > tgLastModified) {
\r
298 } catch (Throwable e) {
\r
299 Logger.defaultLogError(e);
\r
306 * Get all graphs in the Platform
\r
308 * @param collection
\r
309 * @throws IOException
\r
311 public static void getAllGraphs(Collection<GraphBundle> collection) throws IOException {
\r
312 for (Bundle bundle : getBundles()) {
\r
313 GraphBundle entry = getGraph(bundle);
\r
314 if (entry!=null) collection.add(entry);
\r
321 * @param symbolicName
\r
322 * @return bundle or <tt>null</tt> if there is no bundle or graph
\r
323 * @throws IOException
\r
325 public static GraphBundle getGraph(String symbolicName) throws IOException {
\r
326 Bundle bundle = Platform.getBundle(symbolicName);
\r
327 if (bundle == null) return null;
\r
328 return getGraph( bundle );
\r
332 * Read the graph in a graph bundle. Graph is read from "graph.tg" file in the root.
\r
335 * @return transferable graph, or <tt>null</tt> if there is no graph in the bundle.
\r
336 * @throws IOException
\r
338 public static GraphBundleEx getGraph(Bundle bundle) throws IOException {
\r
339 URL url = bundle.getEntry("graph.tg");
\r
341 if (url==null) return null;
\r
342 InputStream is = url.openStream();
\r
343 // NOTE: this is vital for performance.
\r
344 is = new BufferedInputStream(is, 128*1024);
\r
346 DataInput dis = new DataInputStream(is);
\r
348 // dis = new InputStreamReadable(is, <max limit>) to protect from OOM
\r
350 org.simantics.databoard.container.DataContainer container =
\r
351 DataContainers.readFile(dis);
\r
353 Binding binding = TransferableGraph1.BINDING;
\r
354 TransferableGraph1 graph = (TransferableGraph1)container.content.getValue(binding);
\r
355 // TransferableGraph1 graph = (TransferableGraph1) Files.readFile(is, binding);
\r
356 // System.out.println("getGraph(" + bundle.getSymbolicName() + "): read transferable graph in " + (System.nanoTime()-start)*1e-6 + "ms");
\r
357 org.osgi.framework.Version osgiVersion = bundle.getVersion();
\r
358 Version p2Version = Version.createOSGi(osgiVersion.getMajor(), osgiVersion.getMinor(), osgiVersion.getMicro(), osgiVersion.getQualifier());
\r
359 String id = bundle.getSymbolicName();
\r
360 VersionedId vid = new VersionedId(id, p2Version);
\r
361 String name = (String) bundle.getHeaders().get("Bundle-Name");
\r
362 if (name == null) name = id;
\r
363 String immutable = (String) bundle.getHeaders().get("Immutable");
\r
364 boolean isImmutable =
\r
365 immutable != null ?
\r
366 "true".equals(immutable) :
\r
369 // System.out.println("getGraph(" + bundle.getSymbolicName() + "): before hashcode calculation in " + (System.nanoTime()-start)*1e-6 + "ms");
\r
370 GraphBundleEx entry = new GraphBundleEx(name, graph, vid, isImmutable);
\r
371 // System.out.println("getGraph(" + bundle.getSymbolicName() + "): completed in " + (System.nanoTime()-start)*1e-6 + "ms");
\r
373 } catch (SerializationException e) {
\r
374 throw new IOException(e);
\r
375 } catch (IOException e) {
\r
376 throw new IOException("Problem loading graph.tg from bundle " + bundle.getSymbolicName(), e);
\r
377 } catch (RuntimeException e) {
\r
378 throw new IOException("Problem loading graph.tg from bundle " + bundle.getSymbolicName(), e);
\r
379 } catch (AdaptException e) {
\r
380 throw new IOException("Problem loading graph.tg from bundle " + bundle.getSymbolicName(), e);
\r
386 public static class TGInfo {
\r
387 public Bundle bundle;
\r
388 public URL location;
\r
389 public IVersionedId vid;
\r