package org.simantics; import java.io.File; import java.io.IOException; import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; import org.eclipse.equinox.app.IApplication; import org.eclipse.equinox.app.IApplicationContext; import org.eclipse.osgi.service.datalocation.Location; import org.simantics.application.arguments.Arguments; import org.simantics.application.arguments.IArgumentFactory; import org.simantics.application.arguments.IArgumentFactory.NoValueArgumentFactory; import org.simantics.application.arguments.IArgumentFactory.StringArgumentFactory; import org.simantics.application.arguments.IArguments; import org.simantics.application.arguments.SimanticsArguments; import org.simantics.internal.Activator; import org.simantics.scl.runtime.SCLContext; import org.simantics.scl.runtime.reporting.SCLReportingHandler; import org.simantics.scl.runtime.tuple.Tuple0; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Tuukka Lehtonen * @since 1.34.0 */ public class BaselineCreatorApplication implements IApplication { private static final Logger LOGGER = LoggerFactory.getLogger(BaselineCreatorApplication.class); private static final IArgumentFactory OUTPUT = new StringArgumentFactory("-o"); private static final IArgumentFactory VERBOSE = new NoValueArgumentFactory("-v"); /** * The -f argument specifies an SCL function that shall be executed after the * platform has been started and before the platform is shutdown. * *

* The function must be: *

    *
  1. a parameterless function
  2. *
  3. a function with <Proc> effect only
  4. . It must internally invoke * syncWrite etc. to have other effects. *
* *

* This means the function must have the following signature: * func :: () */ private static final IArgumentFactory FUNCTION = new StringArgumentFactory("-f"); IArgumentFactory[] accepted = { SimanticsArguments.RECOVERY_POLICY_FIX_ERRORS, SimanticsArguments.ONTOLOGY_RECOVERY_POLICY_REINSTALL, SimanticsArguments.DISABLE_INDEX, SimanticsArguments.DATABASE_ID, OUTPUT, VERBOSE, FUNCTION, }; private static String currentLocalDateTimeStamp() { return LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd_HHmm")); } private static Path constructOutputPath(Path workspace, IArguments parsedArgs) { if (parsedArgs.contains(OUTPUT)) { return workspace.resolve(parsedArgs.get(OUTPUT)); } else { return workspace.resolve(workspace.getFileName().toString() + "-" + currentLocalDateTimeStamp() + ".zip"); } } private static Path getInstanceLocation() throws CoreException, IOException { Location l = Platform.getInstanceLocation(); if (l == null || l.isReadOnly()) throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Workspace not defined. Use -data argument to define where to place the baselining workspace.")); URL workspaceUrl = l.getURL(); Path workspacePath = new File(workspaceUrl.getPath()).toPath(); Files.createDirectories(workspacePath); return workspacePath; } @Override public Object start(IApplicationContext context) throws Exception { try { Path workspace = getInstanceLocation(); String[] args = (String[]) context.getArguments().get("application.args"); IArguments parsedArgs = Arguments.parse(args, accepted); Path output = constructOutputPath(workspace, parsedArgs); LOGGER.info("Selected output file: {} ", output); // Create database and indexes IProgressMonitor progress = parsedArgs.contains(VERBOSE) ? new TimingProgressMonitor() : new NullProgressMonitor(); Simantics.startUpHeadless(parsedArgs, progress); if (parsedArgs.contains(FUNCTION)) { String func = parsedArgs.get(FUNCTION); String[] moduleAndFunc = splitFunction(func); if (moduleAndFunc != null) { try { LOGGER.info("Invoking SCL function {}/{}", moduleAndFunc[0], moduleAndFunc[1]); SCLContext.getCurrent().put(SCLReportingHandler.REPORTING_HANDLER, SCLReportingHandler.DEFAULT); Simantics.applySCL(moduleAndFunc[0], moduleAndFunc[1], Tuple0.INSTANCE); } catch (Throwable t) { LOGGER.error("Invocation failed", t); } } else { LOGGER.error("SCL function '{}' not invocable for baselining the database", func); } } Simantics.shutdown(progress); // Create the baseline package file Path actualOutput = DatabaseBaselines.packageBaseline(workspace, output); System.out.println("OK " + actualOutput.normalize().toAbsolutePath()); return IApplication.EXIT_OK; } catch (Exception e) { LOGGER.error("Baseline creation failed.", e); throw (Exception) e; } finally { System.exit(0); } } private String[] splitFunction(String func) { int l = func.lastIndexOf('/'); if (l < 0) return null; return new String[] { func.substring(0, l), func.substring(l+1) }; } @Override public void stop() { } }