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.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
+import java.util.Collections;
import java.util.Enumeration;
import java.util.Map.Entry;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Supplier;
import java.util.jar.Attributes;
import java.util.jar.Manifest;
+import java.util.stream.Collectors;
import org.eclipse.core.internal.runtime.PlatformActivator;
import org.eclipse.core.runtime.FileLocator;
import org.eclipse.equinox.p2.metadata.Version;
import org.eclipse.equinox.p2.metadata.VersionedId;
import org.osgi.framework.Bundle;
+import org.simantics.databoard.Bindings;
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.db.common.utils.Logger;
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.compiler.internal.ltk.FileSource;
+import org.simantics.graph.compiler.internal.ltk.ISource;
+import org.simantics.graph.compiler.internal.ltk.Problem;
+import org.simantics.graph.representation.Extensions;
import org.simantics.graph.representation.TransferableGraph1;
-import org.simantics.ltk.FileSource;
-import org.simantics.ltk.ISource;
-import org.simantics.ltk.Problem;
+import org.simantics.graph.representation.TransferableGraphFileReader;
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.
*
public static Bundle[] getBundles() {
return PlatformActivator.getContext().getBundles();
}
-
+
/**
* Get the manifest file of a bundle
*
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();
+ try (InputStream is = url.openStream()) {
+ return new Manifest(is);
}
}
-
+
/**
* Get the manifest file of a bundle
*
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();
+ try (InputStream is = url.openStream()) {
+ return new Manifest(is);
}
}
for (Entry<Object, Object> entry2 : attributes.entrySet()) {
Object key = entry2.getKey();
if (key.toString().contains("Installable-Unit")) {
- String bid = entry2.getValue().toString();
+ String bid = entry2.getValue().toString();
list.add( bid );
}
}
- }
+ }
}
-
+
/**
* Get all transferable graphs in the platform
*
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;
//ignore
}
}
-
+
private static File copyResource(URL url, File targetFile) throws IOException, FileNotFoundException {
FileOutputStream os = null;
InputStream is = null;
uncheckedClose(is);
}
}
-
+
private static File extractLib(URL libURL, String libName) throws FileNotFoundException, IOException {
String tmpDirStr = System.getProperty("java.io.tmpdir");
if (tmpDirStr == null)
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.defaultLogError(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.defaultLogError(e);
+ LOGGER.error("Extraction to " + fileName + " failed, url not found: " + url, e);
} catch (IOException e) {
- Logger.defaultLogError(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);
+ LOGGER.error("Unsupported URL protocol '" + url + "' for reading as file '" + fileName + "'");
}
return null;
}
-
+
+ public static Collection<URL> getTGFiles(Bundle b) {
+ Enumeration<URL> enu = b.findEntries("/", "*.tg", false);
+ if (enu == null)
+ return Collections.emptyList();
+ if (!enu.hasMoreElements())
+ return Collections.emptyList();
+ ArrayList<URL> result = new ArrayList<>();
+ while (enu.hasMoreElements())
+ result.add(enu.nextElement());
+ return result;
+ }
+
public static void compile(Bundle b) throws Exception {
-
- Collection<ISource> sources = new ArrayList<ISource>();
- Collection<TransferableGraph1> dependencies = new ArrayList<TransferableGraph1>();
-
+
+ Collection<ISource> sources = new ArrayList<>();
+ Collection<TransferableGraph1> 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));
+ for (URL url : getTGFiles(b2)) {
+ File graphFile = url2file(url, b2.toString() + url.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.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.defaultLogError(errorStringBuilder.toString());
+ LOGGER.error(errorStringBuilder.toString());
} else {
- DataContainers.writeFile(new File(bundleFile, "graph.tg"),
- new DataContainer("graph", 1, new Variant(TransferableGraph1.BINDING, result.getGraph())));
+ GraphCompiler.write(new File(bundleFile, "graph.tg"), result.getGraph());
}
-
+
}
-
+
/**
* Compile all dynamic ontologies in the Platform
*
}
}
} catch (Throwable e) {
- Logger.defaultLogError(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<GraphBundle> collection) throws IOException {
- for (Bundle bundle : getBundles()) {
- GraphBundle entry = getGraph(bundle);
- if (entry!=null) collection.add(entry);
- }
+ public static Collection<GraphBundle> getAllGraphs() throws IOException {
+ AtomicReference<IOException> problem = new AtomicReference<>();
+
+ Collection<GraphBundle> gbundles = Arrays.stream(getBundles())
+ .parallel()
+ .map(b -> {
+ try {
+ return problem.get() == null ? getGraphs(b) : Collections.<GraphBundleEx>emptyList();
+ } catch (IOException e) {
+ if (LOGGER.isWarnEnabled())
+ LOGGER.debug("Could not get graph from bundle {}", b, e);
+ problem.set(e);
+ return Collections.<GraphBundleEx>emptyList();
+ }
+ })
+ .flatMap(Collection::stream)
+ .collect(Collectors.toList());
+
+ if (problem.get() != null)
+ throw problem.get();
+ return gbundles;
}
/**
if (bundle == null) return null;
return getGraph( bundle );
}
-
+
+ public static Collection<GraphBundleEx> getGraphs(Bundle bundle) throws IOException {
+ return getTGFiles(bundle).stream()
+ .map(url -> {
+ try {
+ GraphBundleEx result = tryGetOnDemandGraph(bundle, url);
+ return result != null ? result : getCompleteGraph(bundle, url);
+ } catch (IOException e) {
+ if (LOGGER.isWarnEnabled())
+ LOGGER.warn("Could not get graph from bundle url {}", url, e);
+ return null;
+ }
+ })
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList());
+ }
+
+ private static String tgFileId(Bundle bundle, URL url) {
+ String urlString = url.toString();
+ String file = urlString.substring(urlString.lastIndexOf("/") + 1);
+ return bundle.getSymbolicName() + "_" + file;
+ }
+
/**
* Read the graph in a graph bundle. Graph is read from "graph.tg" file in the root.
*
*/
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);
+ if (url == null)
+ return null;
+ GraphBundleEx result = tryGetOnDemandGraph(bundle, url);
+ return result != null ? result : getCompleteGraph(bundle, url);
+ }
+
+ private static GraphBundleEx getCompleteGraph(Bundle bundle, URL url) throws IOException {
try {
- DataInput dis = new DataInputStream(is);
- // or
- // dis = new InputStreamReadable(is, <max limit>) 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 (SerializationException e) {
- throw new IOException(e);
- } catch (IOException e) {
- throw new IOException("Problem loading graph.tg from bundle " + bundle.getSymbolicName(), e);
- } catch (RuntimeException e) {
+ String id = tgFileId(bundle, url);
+ return new GraphBundleEx(
+ getBundleName(bundle, id),
+ readTG(url),
+ new VersionedId(id, toP2Version(bundle)),
+ isImmutable(bundle));
+ } catch (Exception e) {
throw new IOException("Problem loading graph.tg from bundle " + bundle.getSymbolicName(), e);
- } catch (AdaptException e) {
+ } catch (Error e) {
+ LOGGER.error("Serious problem loading graph.tg from bundle " + bundle.getSymbolicName(), e);
+ throw e;
+ }
+ }
+
+ /**
+ * Read the graph in a graph bundle. Graph is read from "graph.tg" file in the root.
+ *
+ * @param bundle
+ * @return transferable graph, or <tt>null</tt> if there is no graph in the bundle.
+ * @throws IOException
+ */
+ private static GraphBundleEx tryGetOnDemandGraph(Bundle bundle, URL url) throws IOException {
+ try {
+ Integer cachedHash = readCachedHash(url);
+ if (cachedHash == null) {
+ LOGGER.info("No cached hash for " + bundle);
+ return null;
+ }
+
+ Supplier<TransferableGraph1> graphSource = () -> {
+ try {
+ return readTG(url);
+ } catch (Exception e) {
+ throw new RuntimeException("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;
+ }
+ };
+
+ String id = tgFileId(bundle, url);
+
+ return new GraphBundleEx(
+ getBundleName(bundle, id),
+ graphSource,
+ cachedHash,
+ new VersionedId(id, toP2Version(bundle)),
+ isImmutable(bundle));
+ } catch (Exception e) {
throw new IOException("Problem loading graph.tg from bundle " + bundle.getSymbolicName(), e);
- } finally {
- is.close();
}
}
-
+
+ private static TransferableGraph1 readTG(URL url) throws Exception {
+ try (InputStream is = url.openStream()) {
+ return TransferableGraphFileReader.read(is);
+ }
+ }
+
+ private static DataContainer readHeader(URL url) throws IOException {
+ try (InputStream is = url.openStream()) {
+ return DataContainers.readHeader(new DataInputStream(new BufferedInputStream(is, 1 << 14)));
+ }
+ }
+
+ private static Integer readCachedHash(URL url) throws IOException, AdaptException {
+ DataContainer header = readHeader(url);
+ Variant hashCode = header.metadata.get(Extensions.CACHED_HASHCODE);
+ return hashCode != null ? (Integer) hashCode.getValue(Bindings.INTEGER) : null;
+ }
+
+ private static Version toP2Version(Bundle bundle) {
+ org.osgi.framework.Version osgiVersion = bundle.getVersion();
+ return Version.createOSGi(osgiVersion.getMajor(), osgiVersion.getMinor(), osgiVersion.getMicro(), osgiVersion.getQualifier());
+ }
+
+ private static String getBundleName(Bundle bundle, String id) {
+ String name = (String) bundle.getHeaders().get("Bundle-Name");
+ return name != null ? name : id;
+ }
+
+ private static boolean isImmutable(Bundle bundle) {
+ String immutable = (String) bundle.getHeaders().get("Immutable");
+ return immutable != null ? "true".equals(immutable) : true;
+ }
+
public static class TGInfo {
public Bundle bundle;
public URL location;
public IVersionedId vid;
}
-
-}
+}
\ No newline at end of file