protected boolean parseAsBlock() {
return false;
}
+
+ /*
+ * Override this to provide location information in compilation error situations.
+ */
+ protected String getContextDescription(ReadGraph graph) throws DatabaseException {
+ return toString();
+ }
@SuppressWarnings("unchecked")
private Function1<EvaluationContext, Object> eval(ExpressionEvaluator evaluator, ReadGraph graph) throws DatabaseException {
else
throw e;
} catch (SCLExpressionCompilationException e) {
- LOGGER.error("Could not compile {}", evaluator.getExpressionText(), e);
StringBuilder b = new StringBuilder();
b.append("Couldn't compile '");
b.append(evaluator.getExpressionText());
+ b.append("' in ");
+ b.append(getContextDescription(graph));
b.append("':\n");
StringBuilder b2 = new StringBuilder();
for(CompilationError error : e.getErrors()) {
b2.append(error.description);
b2.append('\n');
}
- System.err.println(b.toString() + b2.toString());
- throw new SCLDatabaseException(b.toString()+b2.toString(), b2.toString(), e.getErrors());
+ SCLDatabaseException exception = new SCLDatabaseException(b.toString()+b2.toString(), b2.toString(), e.getErrors());
+ LOGGER.info(exception.getMessage(), exception);
+ throw exception;
} catch(Throwable e) {
// Should not happen!
LOGGER.error("This error should never happen", e);
import org.simantics.databoard.Bindings;
import org.simantics.db.ReadGraph;
import org.simantics.db.Resource;
+import org.simantics.db.Statement;
import org.simantics.db.common.procedure.adapter.TransientCacheListener;
import org.simantics.db.common.request.IndexRoot;
import org.simantics.db.common.request.PossibleTypedParent;
import org.simantics.db.common.request.UnaryRead;
+import org.simantics.db.common.utils.NameUtils;
import org.simantics.db.exception.DatabaseException;
import org.simantics.db.layer0.scl.AbstractExpressionCompilationContext;
import org.simantics.db.layer0.scl.AbstractExpressionCompilationRequest;
public static Function1<Object, Object> validate(ReadGraph graph, Variable context) throws DatabaseException {
return graph.syncRequest(new ServerSCLValueValidationRequest(graph, context), TransientCacheListener.instance());
}
+
+ @Override
+ protected String getContextDescription(ReadGraph graph) throws DatabaseException {
+ Layer0 L0 = Layer0.getInstance(graph);
+ Statement possibleOwner = graph.getPossibleStatement(literal, L0.IsOwnedBy);
+ if(possibleOwner != null) {
+ String uri = graph.getPossibleURI(possibleOwner.getObject());
+ if(uri != null) {
+ String propertyName = NameUtils.getSafeName(graph, graph.getInverse(possibleOwner.getPredicate()));
+ return uri + "#" + propertyName;
+ }
+ }
+ return super.getContextDescription(graph);
+ }
public static class ServerSCLValueValidationRequest extends ServerSCLValueRequest {
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>\r
+<classpath>\r
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>\r
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>\r
+ <classpathentry kind="src" path="src"/>\r
+ <classpathentry kind="output" path="bin"/>\r
+</classpath>\r
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>\r
+<projectDescription>\r
+ <name>org.simantics.platform.ui</name>\r
+ <comment></comment>\r
+ <projects>\r
+ </projects>\r
+ <buildSpec>\r
+ <buildCommand>\r
+ <name>org.eclipse.jdt.core.javabuilder</name>\r
+ <arguments>\r
+ </arguments>\r
+ </buildCommand>\r
+ <buildCommand>\r
+ <name>org.eclipse.pde.ManifestBuilder</name>\r
+ <arguments>\r
+ </arguments>\r
+ </buildCommand>\r
+ <buildCommand>\r
+ <name>org.eclipse.pde.SchemaBuilder</name>\r
+ <arguments>\r
+ </arguments>\r
+ </buildCommand>\r
+ </buildSpec>\r
+ <natures>\r
+ <nature>org.eclipse.pde.PluginNature</nature>\r
+ <nature>org.eclipse.jdt.core.javanature</nature>\r
+ </natures>\r
+</projectDescription>\r
--- /dev/null
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Platform UI Components
+Bundle-SymbolicName: org.simantics.platform.ui
+Bundle-Version: 1.0.0.qualifier
+Bundle-Vendor: Semantum Oy
+Automatic-Module-Name: org.simantics.platform.ui
+Bundle-RequiredExecutionEnvironment: JavaSE-1.8
+Require-Bundle: org.eclipse.ui.console;bundle-version="3.7.0",
+ org.eclipse.ui;bundle-version="3.109.0",
+ org.eclipse.core.jobs;bundle-version="3.9.0",
+ org.eclipse.jface.text;bundle-version="3.12.0",
+ org.eclipse.core.runtime;bundle-version="3.13.0",
+ org.simantics.ui,
+ org.slf4j.api,
+ ch.qos.logback.core,
+ ch.qos.logback.classic
+Bundle-ActivationPolicy: lazy
+Bundle-Activator: org.simantics.platform.ui.internal.Activator
+Export-Package: org.simantics.platform.ui
--- /dev/null
+source.. = src/\r
+output.. = bin/\r
+bin.includes = META-INF/,\\r
+ .\r
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2019 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:
+ * Semantum Oy - initial API and implementation
+ *******************************************************************************/
+package org.simantics.platform.ui;
+
+import java.io.IOException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.console.ConsolePlugin;
+import org.eclipse.ui.console.IConsole;
+import org.eclipse.ui.console.MessageConsole;
+import org.eclipse.ui.console.MessageConsoleStream;
+
+public class SimanticsConsole extends MessageConsole {
+
+ // We don't want to constantly lose data, but this is still only half MB
+ private static int HIGH_WATERMARK = 2<<18;
+ // 75%
+ private static int LOW_WATERMARK = 3 * (HIGH_WATERMARK>>2);
+
+ private ExecutorService executor = Executors.newSingleThreadExecutor();
+
+ private Color black;
+ private Color red;
+ private Color green;
+ private Color yellow;
+ private Color blue;
+ private Color magenta;
+ private Color cyan;
+ private Color white;
+
+ private MessageConsoleStream stream;
+
+ public SimanticsConsole() {
+ super("Simantics Console", "Simantics Console", null, true);
+ }
+
+ @Override
+ protected void init() {
+ Display d = Display.getDefault();
+
+ black = tuneColor(d, SWT.COLOR_BLACK);
+ red = tuneColor(d, SWT.COLOR_RED);
+ green = tuneColor(d, SWT.COLOR_GREEN);
+ yellow = tuneColor(d, SWT.COLOR_YELLOW);
+ blue = tuneColor(d, SWT.COLOR_BLUE);
+ magenta = tuneColor(d, SWT.COLOR_MAGENTA);
+ cyan = tuneColor(d, SWT.COLOR_CYAN);
+ white = tuneColor(d, SWT.COLOR_WHITE);
+
+ setBackground(black);
+ stream = newMessageStream();
+ stream.setFontStyle(SWT.NORMAL);
+ stream.setColor(white);
+
+ setWaterMarks(LOW_WATERMARK, HIGH_WATERMARK);
+
+ }
+
+ @Override
+ protected void dispose() {
+ super.dispose();
+ executor.shutdown();
+ try {
+ stream.close();
+ } catch (IOException e) {
+ // nothing to do
+ }
+
+ black.dispose();
+ red.dispose();
+ green.dispose();
+ green.dispose();
+ yellow.dispose();
+ blue.dispose();
+ magenta.dispose();
+ cyan.dispose();
+ white.dispose();
+ }
+
+ private Color tuneColor(Display d, int color) {
+ Color c = d.getSystemColor(color);
+ return new Color(d, (int)(c.getRed() * 0.75), (int)(c.getGreen() * 0.75), (int)(c.getBlue() * 0.75));
+ }
+
+ public static SimanticsConsole show() {
+
+ if(!Thread.currentThread().equals(Display.getDefault().getThread())) {
+ Display.getDefault().asyncExec(() -> show());
+ return null;
+ }
+
+ for (IConsole c : ConsolePlugin.getDefault().getConsoleManager().getConsoles()) {
+ if (c instanceof SimanticsConsole) {
+ SimanticsConsole sc = (SimanticsConsole) c;
+ sc.activate();
+ return sc;
+ }
+ }
+
+ SimanticsConsole sc = new SimanticsConsole();
+ ConsolePlugin.getDefault().getConsoleManager().addConsoles(new SimanticsConsole[] {sc});
+ return sc;
+
+ }
+
+ public static SimanticsConsole findConsole() {
+
+ for (IConsole c : ConsolePlugin.getDefault().getConsoleManager().getConsoles()) {
+ if (c instanceof SimanticsConsole) {
+ return (SimanticsConsole) c;
+ }
+ }
+
+ SimanticsConsole sc = new SimanticsConsole();
+ ConsolePlugin.getDefault().getConsoleManager().addConsoles(new SimanticsConsole[] {sc});
+ return sc;
+
+ }
+
+ public void write(String data) {
+ executor.submit(() -> {
+ Pattern p = Pattern.compile("(\\x1b\\[(?<cmd>.*?(m)))");
+ Matcher m = p.matcher(data);
+ int pos = 0;
+ while (m.find()) {
+ if (m.start() > pos) {
+ stream.print(data.substring(pos, m.start()));
+ }
+ pos = m.end();
+
+ String cmd = m.group("cmd");
+ if (cmd.endsWith("m")) {
+ // Stream style can be set only in UI thread
+ Display.getDefault().syncExec(() -> {
+ int fontStyle = stream.getFontStyle();
+ Color color = stream.getColor();
+ try {
+ stream.close();
+ } catch (IOException e) {
+ // nothing to do
+ }
+ stream = newMessageStream();
+
+ String attributes[] = cmd.substring(0, cmd.length() - 1).split(";");
+ for (String attribute : attributes) {
+ switch (Integer.parseInt(attribute)) {
+ case 0: fontStyle = SWT.NORMAL; break;
+ case 1: fontStyle = SWT.BOLD; break;
+ case 4: break; // underline
+ case 5: break; // blink
+ case 7: break; // reverse video
+ case 8: break; // nondisplayed
+ case 30: color = black; break;
+ case 31: color = red; break;
+ case 32: color = green; break;
+ case 33: color = yellow; break;
+ case 34: color = blue; break;
+ case 35: color = magenta; break;
+ case 36: color = cyan; break;
+ case 37: color = white; break;
+ // background colors not supported
+ default:break;
+ }
+ }
+ stream.setColor(color);
+ stream.setFontStyle(fontStyle);
+ });
+ }
+ }
+ if (pos < data.length()) {
+ stream.print(data.substring(pos));
+ }
+ stream.println();
+ });
+ }
+}
\ No newline at end of file
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2019 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:
+ * Semantum Oy - initial API and implementation
+ *******************************************************************************/
+package org.simantics.platform.ui.internal;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.simantics.UnhandledExceptionService;
+import org.simantics.platform.ui.SimanticsConsole;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class Activator extends AbstractUIPlugin {
+
+ public Activator() {
+ }
+
+ @Override
+ public void start(BundleContext context) throws Exception {
+
+ super.start(context);
+
+ SimanticsConsole console = SimanticsConsole.findConsole();
+ if(console != null) {
+ ServiceReference<?> ref = context.getServiceReference(UnhandledExceptionService.class.getName());
+ if (ref != null) {
+ UnhandledExceptionService service = (UnhandledExceptionService) context.getService(ref);
+ service.registerHandler(t -> {
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ t.printStackTrace(pw);
+ console.write(sw.toString());
+ });
+ }
+ ConsoleAppender ca = new ConsoleAppender(console);
+ ch.qos.logback.classic.Logger logbackLogger =
+ (ch.qos.logback.classic.Logger)LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
+ logbackLogger.addAppender(ca);
+ ca.start();
+ }
+
+ }
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2019 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:
+ * Semantum Oy - initial API and implementation
+ *******************************************************************************/
+package org.simantics.platform.ui.internal;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.TimeZone;
+
+import org.simantics.platform.ui.SimanticsConsole;
+
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.core.AppenderBase;
+
+public class ConsoleAppender extends AppenderBase<ILoggingEvent> {
+
+ final private SimanticsConsole console;
+
+ final private DateFormat formatter;
+
+ ConsoleAppender(SimanticsConsole console) {
+ assert(console != null);
+ this.console = console;
+ formatter = new SimpleDateFormat("dd.LL.yyyy HH:mm:ss.SSS");
+ formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
+ }
+
+ @Override
+ protected void append(ILoggingEvent e) {
+ StringBuilder b = new StringBuilder();
+ b.append('[');
+ b.append(formatter.format(new Date(e.getTimeStamp())));
+ b.append("]: ");
+ b.append(e.getMessage());
+ console.write(b.toString());
+ }
+
+}
\ No newline at end of file
import org.simantics.scl.compiler.top.SCLExpressionCompilationException;
import org.simantics.scl.compiler.types.Type;
import org.simantics.structural2.scl.AbstractCompileStructuralValueRequest.CompilationContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* Compiles an SCL expression that is attached to a literal
*/
public abstract class AbstractCompileStructuralValueRequest extends AbstractExpressionCompilationRequest<CompilationContext, Object> {
+ private static final Logger LOGGER = LoggerFactory.getLogger(AbstractCompileStructuralValueRequest.class);
+
protected final Resource relation;
public static class CompilationContext extends AbstractExpressionCompilationContext {
@Override
abstract protected String getExpressionText(ReadGraph graph) throws DatabaseException;
+
+ /*
+ * @throws DatabaseException is an index root could not be found
+ */
abstract protected Resource getIndexRoot(ReadGraph graph) throws DatabaseException;
+ /*
+ * Does not throw if the graph is valid.
+ *
+ * @return null if component type was not availble
+ *
+ */
abstract protected Resource getComponentType(ReadGraph graph) throws DatabaseException;
+
+ protected Resource getPossibleIndexRoot(ReadGraph graph) throws DatabaseException {
+ try {
+ return getIndexRoot(graph);
+ } catch (DatabaseException e) {
+ LOGGER.error("Error while resolving index root during structural value compilation", e);
+ return null;
+ }
+ }
@Override
protected Type getExpectedType(ReadGraph graph, CompilationContext context)
return null;
}
+ @Override
+ protected String getContextDescription(ReadGraph graph) throws DatabaseException {
+ Resource componentType = getComponentType(graph);
+ if(componentType != null) {
+ String uri = graph.getPossibleURI(componentType);
+ if(uri != null)
+ return uri;
+ }
+ // OK, no component type - report index root then
+ Resource indexRoot = getPossibleIndexRoot(graph);
+ if(indexRoot != null) {
+ String uri = graph.getPossibleURI(componentType);
+ if(uri != null)
+ return uri;
+ }
+ // OK, nothing - report default
+ return super.getContextDescription(graph);
+ }
+
}
return true;
}
+ @Override
+ protected String getContextDescription(ReadGraph graph) throws DatabaseException {
+ if(component != null) {
+ String uri = graph.getPossibleURI(component);
+ if(uri != null) {
+ String propertyName = NameUtils.getSafeName(graph, relation);
+ return uri + "#" + propertyName;
+ }
+ }
+ return super.getContextDescription(graph);
+ }
+
}
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.ui.plugin.AbstractUIPlugin;
import org.osgi.framework.BundleContext;
+import org.simantics.internal.UnhandledExceptionServiceImpl;
import org.simantics.ui.workbench.WorkbenchShutdownServiceImpl;
/**
public void start(BundleContext context) throws Exception {
WorkbenchShutdownServiceImpl.initialize(context);
+ UnhandledExceptionServiceImpl.initialize(context);
super.start(context);
public void stop(BundleContext context) throws Exception {
WorkbenchShutdownServiceImpl.close();
+ UnhandledExceptionServiceImpl.close();
super.stop(context);
import org.eclipse.ui.keys.IBindingService;
import org.eclipse.ui.progress.IProgressService;
import org.eclipse.ui.statushandlers.AbstractStatusHandler;
+import org.eclipse.ui.statushandlers.StatusAdapter;
import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.Version;
import org.simantics.CancelStartupException;
import org.simantics.SimanticsPlatform.OntologyRecoveryPolicy;
import org.simantics.SimanticsPlatform.RecoveryPolicy;
import org.simantics.TimingProgressMonitor;
+import org.simantics.UnhandledExceptionService;
import org.simantics.application.arguments.IArguments;
import org.simantics.application.arguments.SimanticsArguments;
import org.simantics.db.common.Indexing;
import org.simantics.ui.jobs.SessionGarbageCollectorJob;
import org.simantics.ui.workbench.PerspectiveBarsActivator;
import org.simantics.ui.workbench.PerspectiveContextActivator;
+import org.simantics.ui.workbench.WorkbenchShutdownService;
import org.simantics.utils.logging.TimeLogger;
import org.simantics.utils.ui.dialogs.ShowError;
import org.simantics.utils.ui.dialogs.ShowMessage;
* location is not being shown
*/
public String getWorkspaceLocation() {
- // read command line, which has priority
- IEclipseContext context = getWorkbenchConfigurer().getWorkbench().getService(IEclipseContext.class);
- String location = context != null ? (String) context.get(E4Workbench.FORCED_SHOW_LOCATION) : null;
- if (location != null) {
- return location;
- }
- // read the preference
- if (IDEWorkbenchPlugin.getDefault().getPreferenceStore().getBoolean(IDEInternalPreferences.SHOW_LOCATION)) {
- return Platform.getLocation().toOSString();
- }
- return null;
+ // read command line, which has priority
+ IEclipseContext context = getWorkbenchConfigurer().getWorkbench().getService(IEclipseContext.class);
+ String location = context != null ? (String) context.get(E4Workbench.FORCED_SHOW_LOCATION) : null;
+ if (location != null) {
+ return location;
+ }
+ // read the preference
+ if (IDEWorkbenchPlugin.getDefault().getPreferenceStore().getBoolean(IDEInternalPreferences.SHOW_LOCATION)) {
+ return Platform.getLocation().toOSString();
+ }
+ return null;
}
/**
*
* @see org.eclipse.ui.application.WorkbenchAdvisor#getWorkbenchErrorHandler()
*/
+
+ private AbstractStatusHandler workbenchErrorHandler = new AbstractStatusHandler() {
+
+ @Override
+ public void handle(StatusAdapter statusAdapter, int style) {
+ if (ideWorkbenchErrorHandler == null) {
+ ideWorkbenchErrorHandler = new IDEWorkbenchErrorHandler(
+ getWorkbenchConfigurer());
+ }
+ ideWorkbenchErrorHandler.handle(statusAdapter, style);
+
+ BundleContext context = Activator.getDefault().getBundle().getBundleContext();
+ ServiceReference<?> ref = context.getServiceReference(UnhandledExceptionService.class.getName());
+ UnhandledExceptionService unhandled = (UnhandledExceptionService)context.getService(ref);
+ Throwable t = statusAdapter.getStatus().getException();
+ if(t != null)
+ unhandled.handle(t);
+
+ }
+ };
+
@Override
public AbstractStatusHandler getWorkbenchErrorHandler() {
- if (ideWorkbenchErrorHandler == null) {
- ideWorkbenchErrorHandler = new IDEWorkbenchErrorHandler(
- getWorkbenchConfigurer());
- }
- return ideWorkbenchErrorHandler;
+ return workbenchErrorHandler;
}
/* (non-Javadoc)
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2019 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:
+ * Semantum Oy - initial API and implementation
+ *******************************************************************************/
+package org.simantics;
+
+import java.util.function.Consumer;
+
+/**
+ * @author Antti Villberg
+ */
+public interface UnhandledExceptionService {
+
+ void registerHandler(Consumer<Throwable> handler);
+ void handle(Throwable t);
+
+}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2019 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:
+ * Semantum Oy - initial API and implementation
+ *******************************************************************************/
+package org.simantics.internal;
+
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.function.Consumer;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+import org.simantics.UnhandledExceptionService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @author Antti Villberg
+ */
+public class UnhandledExceptionServiceImpl implements UnhandledExceptionService {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(UnhandledExceptionServiceImpl.class);
+ private static ServiceRegistration<?> service = null;
+
+ private final List<Consumer<Throwable>> handlers = new ArrayList<>();
+
+ /**
+ * Invoked by the bundle activator to initialize the cache service.
+ *
+ * @param context the bundle context to register the service with
+ */
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ public synchronized static void initialize(BundleContext context) {
+ if (service == null) {
+ service = context.registerService(UnhandledExceptionService.class.getName(), new UnhandledExceptionServiceImpl(), new Hashtable());
+ }
+ }
+
+ /**
+ * Invoked by the bundle activator to close the cache service.
+ */
+ public synchronized static void close() {
+ if (service != null) {
+ service.unregister();
+ service = null;
+ }
+ }
+
+ @Override
+ public synchronized void registerHandler(Consumer<Throwable> handler) {
+ handlers.add(handler);
+ }
+
+ @Override
+ public synchronized void handle(Throwable t) {
+ for (Consumer<Throwable> handler : handlers) {
+ try {
+ handler.accept(t);
+ } catch (Exception e) {
+ handleException(handler, e);
+ } catch (LinkageError e) {
+ handleException(handler, e);
+ } catch (AssertionError e) {
+ handleException(handler, e);
+ }
+ }
+ }
+
+ protected void handleException(Object source, Throwable t) {
+ LOGGER.error("{}: Unhandled exception handler {} caused unexpected exception", getClass().getSimpleName(), source, t);
+ }
+
+}
<module>org.simantics.msvc.runtime.x86_64</module>
<module>org.simantics.nativemem</module>
<module>org.simantics.objmap2</module>
+ <module>org.simantics.platform.ui</module>
<module>org.simantics.platform.ui.ontology</module>
<module>org.simantics.project</module>
<module>org.simantics.project.ontology</module>
version="0.0.0"
unpack="false"/>
+ <plugin
+ id="org.simantics.platform.ui"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ unpack="false"/>
+
</feature>