/******************************************************************************* * 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; import java.io.File; import java.util.UUID; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Platform; import org.simantics.SimanticsPlatform.OntologyRecoveryPolicy; import org.simantics.SimanticsPlatform.RecoveryPolicy; import org.simantics.application.arguments.IArguments; import org.simantics.application.arguments.SimanticsArguments; import org.simantics.db.ReadGraph; import org.simantics.db.RequestProcessor; import org.simantics.db.Resource; import org.simantics.db.Session; import org.simantics.db.WriteGraph; import org.simantics.db.common.Indexing; import org.simantics.db.common.procedure.adapter.ProcedureAdapter; import org.simantics.db.exception.DatabaseException; import org.simantics.db.exception.RuntimeDatabaseException; import org.simantics.db.indexing.IndexUtils; import org.simantics.db.layer0.util.SimanticsClipboard; import org.simantics.db.layer0.util.SimanticsKeys; import org.simantics.db.layer0.variable.Variable; import org.simantics.db.layer0.variable.Variables; import org.simantics.db.management.ISessionContext; import org.simantics.db.management.ISessionContextProvider; import org.simantics.db.management.ISessionContextProviderSource; import org.simantics.db.management.SessionContextProvider; import org.simantics.db.management.SingleSessionContextProviderSource; import org.simantics.db.request.ReadInterface; import org.simantics.db.request.WriteInterface; import org.simantics.db.service.XSupport; import org.simantics.internal.FileServiceImpl; import org.simantics.layer0.Layer0; import org.simantics.project.IProject; import org.simantics.project.ProjectKeys; import org.simantics.scl.compiler.top.ValueNotFound; import org.simantics.scl.osgi.SCLOsgi; import org.simantics.scl.runtime.SCLContext; import org.simantics.scl.runtime.function.Function; import org.simantics.scl.runtime.function.Function1; import org.simantics.scl.runtime.function.Function2; import org.simantics.utils.FileService; import org.simantics.utils.FileUtils; import org.simantics.utils.TempFiles; import org.simantics.utils.threads.ThreadUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A facade for accessing basic Simantics platform services. Usable without a * graphical UI, i.e. in headless contexts. * * TODO: in time, move headless functionality of SimanticsUI into this class but * originals as delegates in SimanticsUI. * * TODO: duplicate of org.simantics.db.layer0.util.Simantics, do something about this!! */ public class Simantics { private static final Logger LOGGER = LoggerFactory.getLogger(Simantics.class); /** * Default database driver ID */ private static final String DEFAULT_DATABASE_DRIVER_ID = "acorn"; private static String defaultDatabaseDriverId = DEFAULT_DATABASE_DRIVER_ID; private static ISessionContextProviderSource providerSource = null; private static volatile FileServiceImpl fileService = null; /** * Sets the database driver to be used by the platform. To have any effect, * this must be set before platform startup. * * @param id driver id */ public static void setDefaultDatabaseDriver(String id) { defaultDatabaseDriverId = id; } /** * Returns currently set default database driver id. */ public static String getDefaultDatabaseDriver() { return defaultDatabaseDriverId; } /** * @param args * @param progress * @return * @throws PlatformException */ public static ISessionContext startUpHeadless(IArguments args, IProgressMonitor progress) throws PlatformException { if (SimanticsPlatform.INSTANCE.sessionContext != null) { throw new RuntimeDatabaseException("Simantics is already up and running."); } RecoveryPolicy workspacePolicy = Platform.inDevelopmentMode() ? RecoveryPolicy.FixError : RecoveryPolicy.ThrowError; OntologyRecoveryPolicy ontologyPolicy = Platform.inDevelopmentMode() ? OntologyRecoveryPolicy.Merge : OntologyRecoveryPolicy.ThrowError; if (args.contains(SimanticsArguments.RECOVERY_POLICY_FIX_ERRORS)) { workspacePolicy = RecoveryPolicy.FixError; ontologyPolicy = OntologyRecoveryPolicy.Merge; } if (args.contains(SimanticsArguments.ONTOLOGY_RECOVERY_POLICY_REINSTALL)) { ontologyPolicy = OntologyRecoveryPolicy.ReinstallDatabase; } if (args.contains(SimanticsArguments.ONTOLOGY_RECOVERY_POLICY_REINSTALL)) { ontologyPolicy = OntologyRecoveryPolicy.ReinstallDatabase; } if (args.contains(SimanticsArguments.DISABLE_INDEX)) { Indexing.setDefaultDependenciesIndexingEnabled(false); } String databaseDriverId = defaultDatabaseDriverId; if (args.contains(SimanticsArguments.DATABASE_ID)) { databaseDriverId = args.get(SimanticsArguments.DATABASE_ID); Simantics.setDefaultDatabaseDriver(databaseDriverId); } int localPort = 0; if (args.contains(SimanticsArguments.LOCAL_SERVER_PORT)) { try { localPort = args.get(SimanticsArguments.LOCAL_SERVER_PORT); } catch (IllegalArgumentException e) { throw new PlatformException("Failed to open database session", e); } } // ServerAddress remoteDatabase = null; // if (args.contains(SimanticsArguments.SERVER)) { // String serverAddress = args.get(SimanticsArguments.SERVER); // try { // remoteDatabase = new ServerAddress(serverAddress); // } catch (IllegalArgumentException e) { // throw new PlatformException("Failed to open database session", e); // } // } return startUpHeadless(progress, workspacePolicy, ontologyPolicy, localPort, databaseDriverId /*, remoteDatabase*/); } /** * @param progress * @param workspacePolicy * @param ontologyPolicy * @param localPort * @param remoteDatabase * @return * @throws PlatformException */ public static ISessionContext startUpHeadless(IProgressMonitor progress, RecoveryPolicy workspacePolicy, OntologyRecoveryPolicy ontologyPolicy, int localPort, String databaseDriverId) throws PlatformException { if (SimanticsPlatform.INSTANCE.sessionContext != null) { throw new RuntimeDatabaseException("Simantics is already up and running."); } // Set session context provider. final ISessionContextProvider provider = new SessionContextProvider(null); ISessionContextProviderSource source = new SingleSessionContextProviderSource(provider); setSessionContextProviderSource(source); org.simantics.db.layer0.internal.SimanticsInternal.setSessionContextProviderSource(source); if (progress == null) progress = new NullProgressMonitor(); return SimanticsPlatform.INSTANCE.startUp(databaseDriverId, progress, workspacePolicy, ontologyPolicy, true, new ConsoleUserAgent()); } /** * @param progress * @throws PlatformException */ public static void shutdown(IProgressMonitor progress) throws PlatformException { SimanticsPlatform.INSTANCE.shutdown(progress); } /** * Queue execution of a runnable. * * @param runnable */ public static void async(Runnable runnable) { ThreadUtils.getBlockingWorkExecutor().execute(runnable); } public static void async(Runnable runnable, int delay, TimeUnit unit) { ThreadUtils.getTimer().schedule(runnable, delay, unit); } public static ScheduledFuture scheduleAtFixedRate(Runnable runnable, int initialDelay, int period, TimeUnit unit) { return ThreadUtils.getTimer().scheduleAtFixedRate(runnable, initialDelay, period, unit); } /** * Queue execution of a non-blocking runnable. Use this method with caution. * A non-blocking runnable nevers locks anything, No Locks, No semaphores, * No Object.wait(), No synchronized() {} blocks. * * @param runnable a non-blocking runnable */ public static void asyncNonblocking(Runnable runnable) { ThreadUtils.getNonBlockingWorkExecutor().execute(runnable); } /** * Schedule execution of a non-blocking runnable. Use this method with caution. * A non-blocking runnable never locks anything, No Locks, No semaphores, * No Object,wait(), No synchronized() {} blocks. * * @param runnable a non-blocking runnable * @param initialDelay * @param period */ public static void asyncNonblocking(Runnable runnable, int initialDelay, int period) { ThreadUtils.getNonBlockingWorkExecutor().scheduleAtFixedRate(runnable, initialDelay, period, TimeUnit.MILLISECONDS); } public static synchronized ISessionContext setSessionContext(ISessionContext ctx) { return getSessionContextProvider().setSessionContext(ctx); } public static void setSessionContextProviderSource(ISessionContextProviderSource source) { if (source == null) throw new IllegalArgumentException("null provider source"); providerSource = source; } public static ISessionContextProviderSource getProviderSource() { if (providerSource == null) throw new IllegalStateException( "providerSource must be initialized by the application before using class Simantics"); return providerSource; } public static ISessionContextProvider getSessionContextProvider() { return getProviderSource().getActive(); } /** * Returns the database session context associated with the currently active * context. This method should be used to retrieve session contexts only * when the client is sure that the correct context is active. * * @return the session context associated with the currently active context * or null if the context has no session context */ public static ISessionContext getSessionContext() { ISessionContextProvider provider = getSessionContextProvider(); return provider != null ? provider.getSessionContext() : null; } /** * Returns the database Session bound to the currently active context. * *

* The method always returns a non-null Session or produces an * IllegalStateException if a Session was not attainable. *

* * @return the Session bound to the currently active workbench window * @throws IllegalStateException if no Session was available */ public static Session getSession() { ISessionContext ctx = getSessionContext(); if (ctx == null) throw new IllegalStateException("Session unavailable, no database session open"); return ctx.getSession(); } public static RequestProcessor getAvailableRequestProcessor() { Object graph = SCLContext.getCurrent().get("graph"); if(graph instanceof ReadGraph) return (RequestProcessor)graph; else return Simantics.getSession(); } /** * Returns the database Session bound to the currently active context. * Differently from {@link #getSession()}, this method returns * null if there is no current Session available. * * @see #getSession() * @return the Session bound to the currently active context or * null */ public static Session peekSession() { ISessionContext ctx = getSessionContext(); return ctx == null ? null : ctx.peekSession(); } /** * @return the currently open and active project as a Resource * @throws IllegalStateException if there is no currently active database * session, which also means there is no active project at the * moment */ public static Resource getProjectResource() { ISessionContext ctx = getSessionContext(); if (ctx == null) throw new IllegalStateException("No current database session"); Resource project = ctx.getHint(SimanticsKeys.KEY_PROJECT); if (project == null) throw new IllegalStateException("No current project resource in session context " + ctx); return project; } /** * @return the currently open and active project as a {@link Resource} */ public static Resource peekProjectResource() { ISessionContext ctx = getSessionContext(); return ctx != null ? ctx.getHint(SimanticsKeys.KEY_PROJECT) : null; } /** * @return the currently open and active project as an {@link IProject} * @throws IllegalStateException if there is no currently active database * session, which also means there is no active project at the * moment */ public static IProject getProject() { ISessionContext ctx = getSessionContext(); if (ctx == null) throw new IllegalStateException("No current database session"); IProject project = ctx.getHint(ProjectKeys.KEY_PROJECT); if (project == null) throw new IllegalStateException("No current project in session context " + ctx); return project; } /** * @return the currently open and active project as an {@link IProject} or * null if there is no active session or project */ public static IProject peekProject() { ISessionContext ctx = getSessionContext(); return ctx == null ? null : ctx.getHint(ProjectKeys.KEY_PROJECT); } // FIXME: once org.simantics.db.layer0.util.Simantics is gone, re-enable this // private static SimanticsClipboard clipboard = SimanticsClipboard.EMPTY; /** * @param content */ public static void setClipboard(SimanticsClipboard content) { // FIXME: once org.simantics.db.layer0.util.Simantics is gone, re-enable this // if (content == null) // throw new NullPointerException("null clipboard content"); // clipboard = content; org.simantics.db.layer0.internal.SimanticsInternal.setClipboard(content); } public static SimanticsClipboard getClipboard() { // FIXME: once org.simantics.db.layer0.util.Simantics is gone, re-enable this //return clipboard; return org.simantics.db.layer0.internal.SimanticsInternal.getClipboard(); } public static Layer0 getLayer0() throws DatabaseException { return Layer0.getInstance(getSession()); } public static T sync(ReadInterface r) throws DatabaseException { return getSession().sync(r); } public static T sync(WriteInterface r) throws DatabaseException { return getSession().sync(r); } public static void async(ReadInterface r) { getSession().async(r, new ProcedureAdapter()); } public static void async(WriteInterface r) { getSession().async(r); } public static void clearTemporaryDirectory() { FileUtils.deleteDir(getTemporaryDirectory()); } public static File getTempfile(String directory, String suffix) { File dir = getTemporaryDirectory(directory); return new File(dir, UUID.randomUUID().toString() + "." + suffix); } public static File getTemporaryDirectory(String directory) { File sub = new File(getTemporaryDirectory(), directory); sub.mkdirs(); return sub; } public static File getTemporaryDirectory() { File workspace = Platform.getLocation().toFile(); File temp = new File(workspace, "tempFiles"); temp.mkdirs(); return temp; } public static TempFiles getTempFiles() { return TEMP_FILES; } private static class TempFilesImpl implements TempFiles { private final TempFilesImpl parent; private final String prefix; private final String fullPrefix; private TempFilesImpl(TempFilesImpl parent, String directory) { this.parent = parent; this.prefix = directory; this.fullPrefix = getPrefix(new StringBuilder()).toString(); } private StringBuilder getPrefix(StringBuilder sb) { if (parent != null) parent.getPrefix(sb); if (prefix != null && !prefix.isEmpty()) sb.append(prefix).append(File.separatorChar); return sb; } @Override public File getRoot() { return Simantics.getTemporaryDirectory(fullPrefix); } @Override public File getTempfile(String directory, String suffix) { return Simantics.getTempfile(fullPrefix.isEmpty() ? directory : fullPrefix + directory, suffix); } @Override public TempFiles subdirectory(String directory) { return new TempFilesImpl(this, directory); } } public static TempFiles TEMP_FILES = new TempFilesImpl(null, null); public static void flushIndexCaches(IProgressMonitor progress, Session session) { try { IndexUtils.flushIndexCaches(progress, session); } catch (Exception e) { LOGGER.error("Flushing index caches failed.", e); } } public static void saveQueries(Session session) { try { XSupport xs = session.getService(XSupport.class); xs.saveQueries(); } catch (Exception e) { LOGGER.error("Saving database queries failed.", e); } } @SuppressWarnings({ "unchecked", "rawtypes" }) public static T applySCL(String module, String function, Object ... args) throws DatabaseException { try { T t = (T)((Function)SCLOsgi.MODULE_REPOSITORY.getValue(module, function)).applyArray(args); return t; } catch (ValueNotFound e) { throw new DatabaseException("SCL Value not found: " + e.name); } catch (Throwable t) { if (t instanceof DatabaseException) throw (DatabaseException) t; throw new DatabaseException(t); } } public static T applySCL(String module, String function, ReadGraph graph, Object ... args) throws DatabaseException { SCLContext sclContext = SCLContext.getCurrent(); Object oldGraph = sclContext.put("graph", graph); try { return applySCL(module, function, args); } catch (DatabaseException dbe) { throw dbe; } catch (Throwable t) { throw new DatabaseException(t); } finally { sclContext.put("graph", oldGraph); } } @SuppressWarnings("unchecked") public static T applySCLWrite(WriteGraph graph, @SuppressWarnings("rawtypes") Function f, Object ... args) throws DatabaseException { SCLContext sclContext = SCLContext.getCurrent(); Object oldGraph = sclContext.put("graph", graph); try { return (T)f.applyArray(args); } catch (Throwable t) { if (t instanceof DatabaseException) throw (DatabaseException) t; throw new DatabaseException(t); } finally { sclContext.put("graph", oldGraph); } } @SuppressWarnings("unchecked") public static T applySCLRead(ReadGraph graph, @SuppressWarnings("rawtypes") Function f, Object ... args) throws DatabaseException { SCLContext sclContext = SCLContext.getCurrent(); Object oldGraph = sclContext.put("graph", graph); try { return (T)f.applyArray(args); } catch (Throwable t) { if (t instanceof DatabaseException) throw (DatabaseException) t; throw new DatabaseException(t); } finally { sclContext.put("graph", oldGraph); } } public static R applySCLWrite(WriteGraph graph, Function1 function, P0 p0) throws DatabaseException { SCLContext sclContext = SCLContext.getCurrent(); Object oldGraph = sclContext.put("graph", graph); try { return function.apply(p0); } catch (Throwable t) { if (t instanceof DatabaseException) throw (DatabaseException) t; throw new DatabaseException(t); } finally { sclContext.put("graph", oldGraph); } } public static R applySCLRead(ReadGraph graph, Function1 function, P0 p0) throws DatabaseException { SCLContext sclContext = SCLContext.getCurrent(); Object oldGraph = sclContext.put("graph", graph); try { return function.apply(p0); } catch (Throwable t) { if (t instanceof DatabaseException) throw (DatabaseException) t; throw new DatabaseException(t); } finally { sclContext.put("graph", oldGraph); } } public static R applySCLRead(ReadGraph graph, Function2 function, P0 p0, P1 p1) throws DatabaseException { SCLContext sclContext = SCLContext.getCurrent(); Object oldGraph = sclContext.put("graph", graph); try { return function.apply(p0, p1); } catch (Throwable t) { if (t instanceof DatabaseException) throw (DatabaseException) t; throw new DatabaseException(t); } finally { sclContext.put("graph", oldGraph); } } public static R invokeSCLWrite(WriteGraph graph, Variable property, P0 p0) throws DatabaseException { Function1 fn = property.getPossibleValue(graph); if(fn == null) throw new DatabaseException("No function for " + property.getURI(graph)); return Simantics.applySCLWrite(graph, fn, p0); } public static R invokeSCL(ReadGraph graph, Variable property, P0 p0) throws DatabaseException { Function1 fn = property.getPossibleValue(graph); if(fn == null) throw new DatabaseException("No function for " + property.getURI(graph)); return Simantics.applySCLRead(graph, fn, p0); } public static R invokeSCL(ReadGraph graph, Variable property, P0 p0, P1 p1) throws DatabaseException { Function2 fn = property.getPossibleValue(graph); if(fn == null) throw new DatabaseException("No function for " + property.getURI(graph)); return Simantics.applySCLRead(graph, fn, p0, p1); } public static R invokeSCLWrite(WriteGraph graph, Resource entity, Resource property, P0 p0) throws DatabaseException { return invokeSCLWrite(graph, getProperty(graph, entity, property), p0); } public static R invokeSCL(ReadGraph graph, Resource entity, Resource property, P0 p0) throws DatabaseException { return invokeSCL(graph, getProperty(graph, entity, property), p0); } public static R invokeSCL(ReadGraph graph, Resource entity, Resource property, P0 p0, P1 p1) throws DatabaseException { return invokeSCL(graph, getProperty(graph, entity, property), p0, p1); } public static R tryInvokeSCL(ReadGraph graph, Resource entity, Resource property, P0 p0) throws DatabaseException { Variable p = Variables.tryGetProperty(graph, entity, property); if (p == null) return null; return invokeSCL(graph, p, p0); } public static R tryInvokeSCL(ReadGraph graph, Resource entity, Resource property, P0 p0, P1 p1) throws DatabaseException { Variable p = Variables.tryGetProperty(graph, entity, property); if (p == null) return null; return invokeSCL(graph, p, p0, p1); } private static Variable getProperty(ReadGraph graph, Resource entity, Resource property) throws DatabaseException { return Variables.getVariable(graph, entity).getProperty(graph, property); } public static boolean ensureMemoryBytes(long bytes) { Runtime runtime = Runtime.getRuntime(); long consumedMemory = runtime.totalMemory() - runtime.freeMemory(); long available = runtime.maxMemory() - consumedMemory; return available > bytes; } public static long getDiskBytes() { File ws = new File(Platform.getInstanceLocation().getURL().getFile()); return ws.getUsableSpace(); } /** * @return a service for dealing with recurring file system handling cases */ public static FileService getFileService() { FileService fs = fileService; if (fs == null) { synchronized (Simantics.class) { fs = fileService; if (fs == null) fs = fileService = new FileServiceImpl(); } } return fs; } }