--- /dev/null
+/*******************************************************************************\r
+ * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
+ * in Industry THTH ry.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ * VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics;\r
+\r
+import java.io.File;\r
+import java.util.UUID;\r
+import java.util.concurrent.ScheduledFuture;\r
+import java.util.concurrent.TimeUnit;\r
+\r
+import org.eclipse.core.runtime.IProgressMonitor;\r
+import org.eclipse.core.runtime.NullProgressMonitor;\r
+import org.eclipse.core.runtime.Platform;\r
+import org.simantics.SimanticsPlatform.OntologyRecoveryPolicy;\r
+import org.simantics.SimanticsPlatform.RecoveryPolicy;\r
+import org.simantics.application.arguments.IArguments;\r
+import org.simantics.application.arguments.SimanticsArguments;\r
+import org.simantics.db.ReadGraph;\r
+import org.simantics.db.Resource;\r
+import org.simantics.db.Session;\r
+import org.simantics.db.WriteGraph;\r
+import org.simantics.db.common.procedure.adapter.ProcedureAdapter;\r
+import org.simantics.db.exception.DatabaseException;\r
+import org.simantics.db.exception.RuntimeDatabaseException;\r
+import org.simantics.db.indexing.IndexUtils;\r
+import org.simantics.db.layer0.util.SimanticsClipboard;\r
+import org.simantics.db.layer0.util.SimanticsKeys;\r
+import org.simantics.db.layer0.variable.Variable;\r
+import org.simantics.db.layer0.variable.Variables;\r
+import org.simantics.db.management.ISessionContext;\r
+import org.simantics.db.management.ISessionContextProvider;\r
+import org.simantics.db.management.ISessionContextProviderSource;\r
+import org.simantics.db.management.SessionContextProvider;\r
+import org.simantics.db.management.SingleSessionContextProviderSource;\r
+import org.simantics.db.request.ReadInterface;\r
+import org.simantics.db.request.WriteInterface;\r
+import org.simantics.internal.FileServiceImpl;\r
+import org.simantics.layer0.Layer0;\r
+import org.simantics.project.IProject;\r
+import org.simantics.project.ProjectKeys;\r
+import org.simantics.scl.compiler.top.ValueNotFound;\r
+import org.simantics.scl.osgi.SCLOsgi;\r
+import org.simantics.scl.runtime.SCLContext;\r
+import org.simantics.scl.runtime.function.Function;\r
+import org.simantics.scl.runtime.function.Function1;\r
+import org.simantics.scl.runtime.function.Function2;\r
+import org.simantics.utils.FileService;\r
+import org.simantics.utils.FileUtils;\r
+import org.simantics.utils.TempFiles;\r
+import org.simantics.utils.threads.ThreadUtils;\r
+\r
+/**\r
+ * A facade for accessing basic Simantics platform services. Usable without a\r
+ * graphical UI, i.e. in headless contexts.\r
+ *\r
+ * TODO: in time, move headless functionality of SimanticsUI into this class but\r
+ * originals as delegates in SimanticsUI.\r
+ *\r
+ * TODO: duplicate of org.simantics.db.layer0.util.Simantics, do something about this!!\r
+ */\r
+public class Simantics {\r
+\r
+ private static ISessionContextProviderSource providerSource = null;\r
+ private static volatile FileServiceImpl fileService = null;\r
+\r
+ /**\r
+ * @param args\r
+ * @param progress\r
+ * @return\r
+ * @throws PlatformException\r
+ */\r
+ public static ISessionContext startUpHeadless(IArguments args, IProgressMonitor progress) throws PlatformException {\r
+ if (SimanticsPlatform.INSTANCE.sessionContext != null) {\r
+ throw new RuntimeDatabaseException("Simantics is already up and running.");\r
+ }\r
+\r
+ RecoveryPolicy workspacePolicy = Platform.inDevelopmentMode() ? RecoveryPolicy.FixError : RecoveryPolicy.ThrowError;\r
+ OntologyRecoveryPolicy ontologyPolicy = Platform.inDevelopmentMode() ? OntologyRecoveryPolicy.Merge : OntologyRecoveryPolicy.ThrowError;\r
+\r
+ if (args.contains(SimanticsArguments.RECOVERY_POLICY_FIX_ERRORS)) {\r
+ workspacePolicy = RecoveryPolicy.FixError;\r
+ ontologyPolicy = OntologyRecoveryPolicy.Merge;\r
+ }\r
+\r
+ if (args.contains(SimanticsArguments.ONTOLOGY_RECOVERY_POLICY_REINSTALL)) {\r
+ ontologyPolicy = OntologyRecoveryPolicy.ReinstallDatabase;\r
+ }\r
+\r
+ if (args.contains(SimanticsArguments.ONTOLOGY_RECOVERY_POLICY_REINSTALL)) {\r
+ ontologyPolicy = OntologyRecoveryPolicy.ReinstallDatabase;\r
+ }\r
+\r
+ int localPort = 0;\r
+ if (args.contains(SimanticsArguments.LOCAL_SERVER_PORT)) {\r
+ try {\r
+ localPort = args.get(SimanticsArguments.LOCAL_SERVER_PORT);\r
+ } catch (IllegalArgumentException e) {\r
+ throw new PlatformException("Failed to open database session", e);\r
+ }\r
+ }\r
+\r
+// ServerAddress remoteDatabase = null;\r
+// if (args.contains(SimanticsArguments.SERVER)) {\r
+// String serverAddress = args.get(SimanticsArguments.SERVER);\r
+// try {\r
+// remoteDatabase = new ServerAddress(serverAddress);\r
+// } catch (IllegalArgumentException e) {\r
+// throw new PlatformException("Failed to open database session", e);\r
+// }\r
+// }\r
+\r
+ return startUpHeadless(progress, workspacePolicy, ontologyPolicy, localPort /*, remoteDatabase*/);\r
+ }\r
+\r
+ /**\r
+ * @param progress\r
+ * @param workspacePolicy\r
+ * @param ontologyPolicy\r
+ * @param localPort\r
+ * @param remoteDatabase\r
+ * @return\r
+ * @throws PlatformException\r
+ */\r
+ public static ISessionContext startUpHeadless(IProgressMonitor progress, RecoveryPolicy workspacePolicy, OntologyRecoveryPolicy ontologyPolicy, int localPort) throws PlatformException {\r
+ if (SimanticsPlatform.INSTANCE.sessionContext != null) {\r
+ throw new RuntimeDatabaseException("Simantics is already up and running.");\r
+ }\r
+\r
+ // Set session context provider.\r
+ final ISessionContextProvider provider = new SessionContextProvider(null);\r
+ ISessionContextProviderSource source = new SingleSessionContextProviderSource(provider);\r
+ setSessionContextProviderSource(source);\r
+ org.simantics.db.layer0.internal.SimanticsInternal.setSessionContextProviderSource(source);\r
+\r
+ if (progress == null)\r
+ progress = new NullProgressMonitor();\r
+ return SimanticsPlatform.INSTANCE.startUp(null, progress, workspacePolicy, ontologyPolicy, true, new ConsoleUserAgent());\r
+ }\r
+\r
+ /**\r
+ * @param progress\r
+ * @throws PlatformException\r
+ */\r
+ public static void shutdown(IProgressMonitor progress) throws PlatformException {\r
+ SimanticsPlatform.INSTANCE.shutdown(progress);\r
+ }\r
+\r
+ /**\r
+ * Queue execution of a runnable.\r
+ *\r
+ * @param runnable\r
+ */\r
+ public static void async(Runnable runnable) {\r
+ ThreadUtils.getBlockingWorkExecutor().execute(runnable);\r
+ }\r
+\r
+ public static void async(Runnable runnable, int delay, TimeUnit unit) {\r
+ ThreadUtils.getTimer().schedule(runnable, delay, unit);\r
+ }\r
+\r
+ public static ScheduledFuture<?> scheduleAtFixedRate(Runnable runnable, int initialDelay, int period, TimeUnit unit) {\r
+ return ThreadUtils.getTimer().scheduleAtFixedRate(runnable, initialDelay, period, unit);\r
+ }\r
+\r
+ /**\r
+ * Queue execution of a non-blocking runnable. Use this method with caution.\r
+ * A non-blocking runnable nevers locks anything, No Locks, No semaphores,\r
+ * No Object.wait(), No synchronized() {} blocks.\r
+ *\r
+ * @param runnable a non-blocking runnable\r
+ */\r
+ public static void asyncNonblocking(Runnable runnable) {\r
+ ThreadUtils.getNonBlockingWorkExecutor().execute(runnable);\r
+ }\r
+\r
+ /**\r
+ * Schedule execution of a non-blocking runnable. Use this method with caution.\r
+ * A non-blocking runnable never locks anything, No Locks, No semaphores,\r
+ * No Object,wait(), No synchronized() {} blocks.\r
+ *\r
+ * @param runnable a non-blocking runnable\r
+ * @param initialDelay\r
+ * @param period\r
+ */\r
+ public static void asyncNonblocking(Runnable runnable, int initialDelay, int period) {\r
+ ThreadUtils.getNonBlockingWorkExecutor().scheduleAtFixedRate(runnable, initialDelay, period, TimeUnit.MILLISECONDS);\r
+ }\r
+\r
+ public static synchronized ISessionContext setSessionContext(ISessionContext ctx) {\r
+ return getSessionContextProvider().setSessionContext(ctx);\r
+ }\r
+\r
+ public static void setSessionContextProviderSource(ISessionContextProviderSource source) {\r
+ if (source == null)\r
+ throw new IllegalArgumentException("null provider source");\r
+ providerSource = source;\r
+ }\r
+\r
+ public static ISessionContextProviderSource getProviderSource() {\r
+ if (providerSource == null)\r
+ throw new IllegalStateException(\r
+ "providerSource must be initialized by the application before using class Simantics");\r
+ return providerSource;\r
+ }\r
+\r
+ public static ISessionContextProvider getSessionContextProvider() {\r
+ return getProviderSource().getActive();\r
+ }\r
+\r
+ /**\r
+ * Returns the database session context associated with the currently active\r
+ * context. This method should be used to retrieve session contexts only\r
+ * when the client is sure that the correct context is active.\r
+ *\r
+ * @return the session context associated with the currently active context\r
+ * or <code>null</code> if the context has no session context\r
+ */\r
+ public static ISessionContext getSessionContext() {\r
+ ISessionContextProvider provider = getSessionContextProvider();\r
+ return provider != null ? provider.getSessionContext() : null;\r
+ }\r
+\r
+ /**\r
+ * Returns the database Session bound to the currently active context.\r
+ *\r
+ * <p>\r
+ * The method always returns a non-null Session or produces an\r
+ * IllegalStateException if a Session was not attainable.\r
+ * </p>\r
+ *\r
+ * @return the Session bound to the currently active workbench window\r
+ * @throws IllegalStateException if no Session was available\r
+ */\r
+ public static Session getSession() {\r
+ ISessionContext ctx = getSessionContext();\r
+ if (ctx == null)\r
+ throw new IllegalStateException("Session unavailable, no database session open");\r
+ return ctx.getSession();\r
+ }\r
+\r
+ /**\r
+ * Returns the database Session bound to the currently active context.\r
+ * Differently from {@link #getSession()}, this method returns\r
+ * <code>null</code> if there is no current Session available.\r
+ *\r
+ * @see #getSession()\r
+ * @return the Session bound to the currently active context or\r
+ * <code>null</code>\r
+ */\r
+ public static Session peekSession() {\r
+ ISessionContext ctx = getSessionContext();\r
+ return ctx == null ? null : ctx.peekSession();\r
+ }\r
+\r
+ /**\r
+ * @return the currently open and active project as a Resource\r
+ * @throws IllegalStateException if there is no currently active database\r
+ * session, which also means there is no active project at the\r
+ * moment\r
+ */\r
+ public static Resource getProjectResource() {\r
+ ISessionContext ctx = getSessionContext();\r
+ if (ctx == null)\r
+ throw new IllegalStateException("No current database session");\r
+ Resource project = ctx.getHint(SimanticsKeys.KEY_PROJECT);\r
+ if (project == null)\r
+ throw new IllegalStateException("No current project resource in session context " + ctx);\r
+ return project;\r
+ }\r
+\r
+ /**\r
+ * @return the currently open and active project as a {@link Resource}\r
+ */\r
+ public static Resource peekProjectResource() {\r
+ ISessionContext ctx = getSessionContext();\r
+ return ctx != null ? ctx.<Resource>getHint(SimanticsKeys.KEY_PROJECT) : null;\r
+ }\r
+\r
+ /**\r
+ * @return the currently open and active project as an {@link IProject}\r
+ * @throws IllegalStateException if there is no currently active database\r
+ * session, which also means there is no active project at the\r
+ * moment\r
+ */\r
+ public static IProject getProject() {\r
+ ISessionContext ctx = getSessionContext();\r
+ if (ctx == null)\r
+ throw new IllegalStateException("No current database session");\r
+ IProject project = ctx.getHint(ProjectKeys.KEY_PROJECT);\r
+ if (project == null)\r
+ throw new IllegalStateException("No current project in session context " + ctx);\r
+ return project;\r
+ }\r
+\r
+ /**\r
+ * @return the currently open and active project as an {@link IProject} or\r
+ * <code>null</code> if there is no active session or project\r
+ */\r
+ public static IProject peekProject() {\r
+ ISessionContext ctx = getSessionContext();\r
+ return ctx == null ? null : ctx.<IProject>getHint(ProjectKeys.KEY_PROJECT);\r
+ }\r
+\r
+ // FIXME: once org.simantics.db.layer0.util.Simantics is gone, re-enable this\r
+// private static SimanticsClipboard clipboard = SimanticsClipboard.EMPTY;\r
+\r
+ /**\r
+ * @param content\r
+ */\r
+ public static void setClipboard(SimanticsClipboard content) {\r
+ // FIXME: once org.simantics.db.layer0.util.Simantics is gone, re-enable this\r
+// if (content == null)\r
+// throw new NullPointerException("null clipboard content");\r
+// clipboard = content;\r
+ org.simantics.db.layer0.internal.SimanticsInternal.setClipboard(content);\r
+ }\r
+\r
+ public static SimanticsClipboard getClipboard() {\r
+ // FIXME: once org.simantics.db.layer0.util.Simantics is gone, re-enable this\r
+ //return clipboard;\r
+ return org.simantics.db.layer0.internal.SimanticsInternal.getClipboard();\r
+ }\r
+\r
+ public static Layer0 getLayer0() throws DatabaseException {\r
+ return Layer0.getInstance(getSession());\r
+ }\r
+\r
+ public static <T> T sync(ReadInterface<T> r) throws DatabaseException {\r
+ return getSession().sync(r);\r
+ }\r
+\r
+ public static <T> T sync(WriteInterface<T> r) throws DatabaseException {\r
+ return getSession().sync(r);\r
+ }\r
+\r
+ public static <T> void async(ReadInterface<T> r) {\r
+ getSession().async(r, new ProcedureAdapter<T>());\r
+ }\r
+\r
+ public static <T> void async(WriteInterface<T> r) {\r
+ getSession().async(r);\r
+ }\r
+\r
+ public static void clearTemporaryDirectory() {\r
+ FileUtils.deleteDir(getTemporaryDirectory());\r
+ }\r
+\r
+ public static File getTempfile(String directory, String suffix) {\r
+ File dir = getTemporaryDirectory(directory);\r
+ return new File(dir, UUID.randomUUID().toString() + "." + suffix);\r
+ }\r
+\r
+ public static File getTemporaryDirectory(String directory) {\r
+ File sub = new File(getTemporaryDirectory(), directory);\r
+ sub.mkdirs();\r
+ return sub;\r
+ }\r
+\r
+ public static File getTemporaryDirectory() {\r
+ File workspace = Platform.getLocation().toFile();\r
+ File temp = new File(workspace, "tempFiles");\r
+ temp.mkdirs();\r
+ return temp;\r
+ }\r
+\r
+ public static TempFiles getTempFiles() {\r
+ return TEMP_FILES;\r
+ }\r
+\r
+ private static class TempFilesImpl implements TempFiles {\r
+ private final TempFilesImpl parent;\r
+ private final String prefix;\r
+ private final String fullPrefix;\r
+\r
+ private TempFilesImpl(TempFilesImpl parent, String directory) {\r
+ this.parent = parent;\r
+ this.prefix = directory;\r
+ this.fullPrefix = getPrefix(new StringBuilder()).toString();\r
+ }\r
+\r
+ private StringBuilder getPrefix(StringBuilder sb) {\r
+ if (parent != null)\r
+ parent.getPrefix(sb);\r
+ if (prefix != null && !prefix.isEmpty())\r
+ sb.append(prefix).append(File.separatorChar);\r
+ return sb;\r
+ }\r
+\r
+ @Override\r
+ public File getRoot() {\r
+ return Simantics.getTemporaryDirectory(fullPrefix);\r
+ }\r
+\r
+ @Override\r
+ public File getTempfile(String directory, String suffix) {\r
+ return Simantics.getTempfile(fullPrefix.isEmpty() ? directory : fullPrefix + directory, suffix);\r
+ }\r
+\r
+ @Override\r
+ public TempFiles subdirectory(String directory) {\r
+ return new TempFilesImpl(this, directory);\r
+ }\r
+ }\r
+\r
+ public static TempFiles TEMP_FILES = new TempFilesImpl(null, null);\r
+\r
+ public static void flushIndexCaches(IProgressMonitor progress, Session session) {\r
+ try {\r
+ IndexUtils.flushIndexCaches(progress, session);\r
+ } catch (Exception e) {\r
+ Logger.defaultLogError(e);\r
+ }\r
+ }\r
+\r
+\r
+ @SuppressWarnings({ "unchecked", "rawtypes" })\r
+ public static <T> T applySCL(String module, String function, ReadGraph graph, Object ... args) throws DatabaseException {\r
+ SCLContext sclContext = SCLContext.getCurrent();\r
+ Object oldGraph = sclContext.put("graph", graph);\r
+ try {\r
+ T t = (T)((Function)SCLOsgi.MODULE_REPOSITORY.getValue(module, function)).applyArray(args);\r
+ return t;\r
+ } catch (ValueNotFound e) {\r
+ throw new DatabaseException("SCL Value not found: " + e.name);\r
+ } catch (Throwable t) {\r
+ throw new DatabaseException(t);\r
+ } finally {\r
+ sclContext.put("graph", oldGraph);\r
+ }\r
+\r
+ }\r
+\r
+ @SuppressWarnings("unchecked")\r
+ public static <T> T applySCLWrite(WriteGraph graph, @SuppressWarnings("rawtypes") Function f, Object ... args) throws DatabaseException {\r
+ SCLContext sclContext = SCLContext.getCurrent();\r
+ Object oldGraph = sclContext.put("graph", graph);\r
+ try {\r
+ return (T)f.applyArray(args);\r
+ } catch (Throwable t) {\r
+ throw new DatabaseException(t);\r
+ } finally {\r
+ sclContext.put("graph", oldGraph);\r
+ }\r
+ }\r
+\r
+ @SuppressWarnings("unchecked")\r
+ public static <T> T applySCLRead(ReadGraph graph, @SuppressWarnings("rawtypes") Function f, Object ... args) throws DatabaseException {\r
+ SCLContext sclContext = SCLContext.getCurrent();\r
+ Object oldGraph = sclContext.put("graph", graph);\r
+ try {\r
+ return (T)f.applyArray(args);\r
+ } catch (Throwable t) {\r
+ throw new DatabaseException(t);\r
+ } finally {\r
+ sclContext.put("graph", oldGraph);\r
+ }\r
+ }\r
+ \r
+ public static <P0,R> R applySCLWrite(WriteGraph graph, Function1<P0,R> function, P0 p0) throws DatabaseException {\r
+ SCLContext sclContext = SCLContext.getCurrent();\r
+ Object oldGraph = sclContext.put("graph", graph);\r
+ try {\r
+ return function.apply(p0);\r
+ } catch (Throwable t) {\r
+ throw new DatabaseException(t);\r
+ } finally {\r
+ sclContext.put("graph", oldGraph);\r
+ }\r
+ }\r
+\r
+ public static <P0,R> R applySCLRead(ReadGraph graph, Function1<P0,R> function, P0 p0) throws DatabaseException {\r
+ SCLContext sclContext = SCLContext.getCurrent();\r
+ Object oldGraph = sclContext.put("graph", graph);\r
+ try {\r
+ return function.apply(p0);\r
+ } catch (Throwable t) {\r
+ throw new DatabaseException(t);\r
+ } finally {\r
+ sclContext.put("graph", oldGraph);\r
+ }\r
+ }\r
+\r
+ public static <P0,P1,R> R applySCLRead(ReadGraph graph, Function2<P0,P1,R> function, P0 p0, P1 p1) throws DatabaseException {\r
+ SCLContext sclContext = SCLContext.getCurrent();\r
+ Object oldGraph = sclContext.put("graph", graph);\r
+ try {\r
+ return function.apply(p0, p1);\r
+ } catch (Throwable t) {\r
+ throw new DatabaseException(t);\r
+ } finally {\r
+ sclContext.put("graph", oldGraph);\r
+ }\r
+ }\r
+\r
+ public static <P0,R> R invokeSCLWrite(WriteGraph graph, Variable property, P0 p0) throws DatabaseException {\r
+ Function1<P0,R> fn = property.getPossibleValue(graph);\r
+ if(fn == null) throw new DatabaseException("No function for " + property.getURI(graph));\r
+ return Simantics.applySCLWrite(graph, fn, p0);\r
+ }\r
+\r
+ public static <P0,R> R invokeSCL(ReadGraph graph, Variable property, P0 p0) throws DatabaseException {\r
+ Function1<P0,R> fn = property.getPossibleValue(graph);\r
+ if(fn == null) throw new DatabaseException("No function for " + property.getURI(graph));\r
+ return Simantics.applySCLRead(graph, fn, p0);\r
+ }\r
+\r
+ public static <P0,P1,R> R invokeSCL(ReadGraph graph, Variable property, P0 p0, P1 p1) throws DatabaseException {\r
+ Function2<P0,P1,R> fn = property.getPossibleValue(graph);\r
+ if(fn == null) throw new DatabaseException("No function for " + property.getURI(graph));\r
+ return Simantics.applySCLRead(graph, fn, p0, p1);\r
+ }\r
+\r
+ public static <P0,R> R invokeSCLWrite(WriteGraph graph, Resource entity, Resource property, P0 p0) throws DatabaseException {\r
+ return invokeSCLWrite(graph, getProperty(graph, entity, property), p0);\r
+ }\r
+\r
+ public static <P0,R> R invokeSCL(ReadGraph graph, Resource entity, Resource property, P0 p0) throws DatabaseException {\r
+ return invokeSCL(graph, getProperty(graph, entity, property), p0);\r
+ }\r
+\r
+ public static <P0,P1,R> R invokeSCL(ReadGraph graph, Resource entity, Resource property, P0 p0, P1 p1) throws DatabaseException {\r
+ return invokeSCL(graph, getProperty(graph, entity, property), p0, p1);\r
+ }\r
+\r
+ public static <P0,R> R tryInvokeSCL(ReadGraph graph, Resource entity, Resource property, P0 p0) throws DatabaseException {\r
+ Variable p = Variables.tryGetProperty(graph, entity, property);\r
+ if (p == null)\r
+ return null;\r
+ return invokeSCL(graph, p, p0);\r
+ }\r
+\r
+ public static <P0,P1,R> R tryInvokeSCL(ReadGraph graph, Resource entity, Resource property, P0 p0, P1 p1) throws DatabaseException {\r
+ Variable p = Variables.tryGetProperty(graph, entity, property);\r
+ if (p == null)\r
+ return null;\r
+ return invokeSCL(graph, p, p0, p1);\r
+ }\r
+\r
+ private static Variable getProperty(ReadGraph graph, Resource entity, Resource property) throws DatabaseException {\r
+ return Variables.getVariable(graph, entity).getProperty(graph, property);\r
+ }\r
+\r
+ public static boolean ensureMemoryBytes(long bytes) {\r
+\r
+ Runtime runtime = Runtime.getRuntime();\r
+ long consumedMemory = runtime.totalMemory() - runtime.freeMemory();\r
+ long available = runtime.maxMemory() - consumedMemory;\r
+\r
+ return available > bytes;\r
+\r
+ }\r
+\r
+ public static long getDiskBytes() {\r
+\r
+ File ws = new File(Platform.getInstanceLocation().getURL().getFile());\r
+ return ws.getUsableSpace();\r
+\r
+ }\r
+\r
+ /**\r
+ * @return a service for dealing with recurring file system handling cases\r
+ */\r
+ public static FileService getFileService() {\r
+ FileService fs = fileService;\r
+ if (fs == null) {\r
+ synchronized (Simantics.class) {\r
+ fs = fileService;\r
+ if (fs == null)\r
+ fs = fileService = new FileServiceImpl();\r
+ }\r
+ }\r
+ return fs;\r
+ }\r
+\r
+}\r