From 04bf1d8e31c85530bcd47d41051362533997134e Mon Sep 17 00:00:00 2001 From: Antti Villberg Date: Thu, 17 Oct 2019 10:49:30 +0300 Subject: [PATCH] Simantics Console gitlab #400 Change-Id: I18d73b4c9a8d3a4e5b3d3fd43455e3fbf44eceba --- .../AbstractExpressionCompilationRequest.java | 15 +- .../server/request/ServerSCLValueRequest.java | 16 ++ bundles/org.simantics.platform.ui/.classpath | 7 + bundles/org.simantics.platform.ui/.project | 28 +++ .../META-INF/MANIFEST.MF | 20 ++ .../build.properties | 4 + .../platform/ui/SimanticsConsole.java | 191 ++++++++++++++++++ .../platform/ui/internal/Activator.java | 55 +++++ .../platform/ui/internal/ConsoleAppender.java | 47 +++++ ...AbstractCompileStructuralValueRequest.java | 42 ++++ .../scl/CompileStructuralValueRequest.java | 12 ++ .../workbench/internal/Activator.java | 3 + .../internal/SimanticsWorkbenchAdvisor.java | 53 +++-- .../simantics/UnhandledExceptionService.java | 24 +++ .../UnhandledExceptionServiceImpl.java | 81 ++++++++ bundles/pom.xml | 1 + .../feature.xml | 7 + 17 files changed, 587 insertions(+), 19 deletions(-) create mode 100644 bundles/org.simantics.platform.ui/.classpath create mode 100644 bundles/org.simantics.platform.ui/.project create mode 100644 bundles/org.simantics.platform.ui/META-INF/MANIFEST.MF create mode 100644 bundles/org.simantics.platform.ui/build.properties create mode 100644 bundles/org.simantics.platform.ui/src/org/simantics/platform/ui/SimanticsConsole.java create mode 100644 bundles/org.simantics.platform.ui/src/org/simantics/platform/ui/internal/Activator.java create mode 100644 bundles/org.simantics.platform.ui/src/org/simantics/platform/ui/internal/ConsoleAppender.java create mode 100644 bundles/org.simantics/src/org/simantics/UnhandledExceptionService.java create mode 100644 bundles/org.simantics/src/org/simantics/internal/UnhandledExceptionServiceImpl.java diff --git a/bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/scl/AbstractExpressionCompilationRequest.java b/bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/scl/AbstractExpressionCompilationRequest.java index 3fcdf66ab..8d364c737 100644 --- a/bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/scl/AbstractExpressionCompilationRequest.java +++ b/bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/scl/AbstractExpressionCompilationRequest.java @@ -147,6 +147,13 @@ implements Read> { 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 eval(ExpressionEvaluator evaluator, ReadGraph graph) throws DatabaseException { @@ -160,18 +167,20 @@ implements Read> { 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); diff --git a/bundles/org.simantics.document.server/src/org/simantics/document/server/request/ServerSCLValueRequest.java b/bundles/org.simantics.document.server/src/org/simantics/document/server/request/ServerSCLValueRequest.java index dce4b34ab..adff8dab5 100644 --- a/bundles/org.simantics.document.server/src/org/simantics/document/server/request/ServerSCLValueRequest.java +++ b/bundles/org.simantics.document.server/src/org/simantics/document/server/request/ServerSCLValueRequest.java @@ -6,10 +6,12 @@ import java.util.Map; 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; @@ -274,6 +276,20 @@ public class ServerSCLValueRequest extends AbstractExpressionCompilationRequest< public static Function1 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 { diff --git a/bundles/org.simantics.platform.ui/.classpath b/bundles/org.simantics.platform.ui/.classpath new file mode 100644 index 000000000..b862a296d --- /dev/null +++ b/bundles/org.simantics.platform.ui/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/bundles/org.simantics.platform.ui/.project b/bundles/org.simantics.platform.ui/.project new file mode 100644 index 000000000..f85097827 --- /dev/null +++ b/bundles/org.simantics.platform.ui/.project @@ -0,0 +1,28 @@ + + + org.simantics.platform.ui + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/bundles/org.simantics.platform.ui/META-INF/MANIFEST.MF b/bundles/org.simantics.platform.ui/META-INF/MANIFEST.MF new file mode 100644 index 000000000..f0e776e41 --- /dev/null +++ b/bundles/org.simantics.platform.ui/META-INF/MANIFEST.MF @@ -0,0 +1,20 @@ +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 diff --git a/bundles/org.simantics.platform.ui/build.properties b/bundles/org.simantics.platform.ui/build.properties new file mode 100644 index 000000000..41eb6ade2 --- /dev/null +++ b/bundles/org.simantics.platform.ui/build.properties @@ -0,0 +1,4 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + . diff --git a/bundles/org.simantics.platform.ui/src/org/simantics/platform/ui/SimanticsConsole.java b/bundles/org.simantics.platform.ui/src/org/simantics/platform/ui/SimanticsConsole.java new file mode 100644 index 000000000..c7aa2e185 --- /dev/null +++ b/bundles/org.simantics.platform.ui/src/org/simantics/platform/ui/SimanticsConsole.java @@ -0,0 +1,191 @@ +/******************************************************************************* + * 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\\[(?.*?(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 diff --git a/bundles/org.simantics.platform.ui/src/org/simantics/platform/ui/internal/Activator.java b/bundles/org.simantics.platform.ui/src/org/simantics/platform/ui/internal/Activator.java new file mode 100644 index 000000000..50c4ff590 --- /dev/null +++ b/bundles/org.simantics.platform.ui/src/org/simantics/platform/ui/internal/Activator.java @@ -0,0 +1,55 @@ +/******************************************************************************* + * 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(); + } + + } + +} diff --git a/bundles/org.simantics.platform.ui/src/org/simantics/platform/ui/internal/ConsoleAppender.java b/bundles/org.simantics.platform.ui/src/org/simantics/platform/ui/internal/ConsoleAppender.java new file mode 100644 index 000000000..8c3bf3705 --- /dev/null +++ b/bundles/org.simantics.platform.ui/src/org/simantics/platform/ui/internal/ConsoleAppender.java @@ -0,0 +1,47 @@ +/******************************************************************************* + * 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 { + + 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 diff --git a/bundles/org.simantics.structural2/src/org/simantics/structural2/scl/AbstractCompileStructuralValueRequest.java b/bundles/org.simantics.structural2/src/org/simantics/structural2/scl/AbstractCompileStructuralValueRequest.java index c4b4e341f..fce1abaa2 100644 --- a/bundles/org.simantics.structural2/src/org/simantics/structural2/scl/AbstractCompileStructuralValueRequest.java +++ b/bundles/org.simantics.structural2/src/org/simantics/structural2/scl/AbstractCompileStructuralValueRequest.java @@ -26,6 +26,8 @@ import org.simantics.scl.compiler.runtime.RuntimeEnvironment; 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 @@ -35,6 +37,8 @@ import org.simantics.structural2.scl.AbstractCompileStructuralValueRequest.Compi */ public abstract class AbstractCompileStructuralValueRequest extends AbstractExpressionCompilationRequest { + private static final Logger LOGGER = LoggerFactory.getLogger(AbstractCompileStructuralValueRequest.class); + protected final Resource relation; public static class CompilationContext extends AbstractExpressionCompilationContext { @@ -53,8 +57,27 @@ public abstract class AbstractCompileStructuralValueRequest extends AbstractExpr @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) @@ -135,4 +158,23 @@ public abstract class AbstractCompileStructuralValueRequest extends AbstractExpr 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); + } + } diff --git a/bundles/org.simantics.structural2/src/org/simantics/structural2/scl/CompileStructuralValueRequest.java b/bundles/org.simantics.structural2/src/org/simantics/structural2/scl/CompileStructuralValueRequest.java index 5e564cc91..ce824090d 100644 --- a/bundles/org.simantics.structural2/src/org/simantics/structural2/scl/CompileStructuralValueRequest.java +++ b/bundles/org.simantics.structural2/src/org/simantics/structural2/scl/CompileStructuralValueRequest.java @@ -118,4 +118,16 @@ public class CompileStructuralValueRequest extends AbstractCompileStructuralValu 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); + } + } diff --git a/bundles/org.simantics.workbench/src/org/simantics/workbench/internal/Activator.java b/bundles/org.simantics.workbench/src/org/simantics/workbench/internal/Activator.java index db6cdac80..95ab99299 100644 --- a/bundles/org.simantics.workbench/src/org/simantics/workbench/internal/Activator.java +++ b/bundles/org.simantics.workbench/src/org/simantics/workbench/internal/Activator.java @@ -16,6 +16,7 @@ import org.eclipse.core.runtime.Status; 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; /** @@ -39,6 +40,7 @@ public class Activator extends AbstractUIPlugin { public void start(BundleContext context) throws Exception { WorkbenchShutdownServiceImpl.initialize(context); + UnhandledExceptionServiceImpl.initialize(context); super.start(context); @@ -51,6 +53,7 @@ public class Activator extends AbstractUIPlugin { public void stop(BundleContext context) throws Exception { WorkbenchShutdownServiceImpl.close(); + UnhandledExceptionServiceImpl.close(); super.stop(context); diff --git a/bundles/org.simantics.workbench/src/org/simantics/workbench/internal/SimanticsWorkbenchAdvisor.java b/bundles/org.simantics.workbench/src/org/simantics/workbench/internal/SimanticsWorkbenchAdvisor.java index cb1ee16d3..f5bda18b0 100644 --- a/bundles/org.simantics.workbench/src/org/simantics/workbench/internal/SimanticsWorkbenchAdvisor.java +++ b/bundles/org.simantics.workbench/src/org/simantics/workbench/internal/SimanticsWorkbenchAdvisor.java @@ -82,7 +82,9 @@ import org.eclipse.ui.internal.progress.ProgressMonitorJobsDialog; 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; @@ -92,6 +94,7 @@ import org.simantics.SimanticsPlatform; 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; @@ -102,6 +105,7 @@ import org.simantics.ui.SimanticsUI; 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; @@ -1133,17 +1137,17 @@ public class SimanticsWorkbenchAdvisor extends WorkbenchAdvisor { * 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; } /** @@ -1175,13 +1179,30 @@ public class SimanticsWorkbenchAdvisor extends WorkbenchAdvisor { * * @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) diff --git a/bundles/org.simantics/src/org/simantics/UnhandledExceptionService.java b/bundles/org.simantics/src/org/simantics/UnhandledExceptionService.java new file mode 100644 index 000000000..d09485161 --- /dev/null +++ b/bundles/org.simantics/src/org/simantics/UnhandledExceptionService.java @@ -0,0 +1,24 @@ +/******************************************************************************* + * 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 handler); + void handle(Throwable t); + +} diff --git a/bundles/org.simantics/src/org/simantics/internal/UnhandledExceptionServiceImpl.java b/bundles/org.simantics/src/org/simantics/internal/UnhandledExceptionServiceImpl.java new file mode 100644 index 000000000..4276dc9e8 --- /dev/null +++ b/bundles/org.simantics/src/org/simantics/internal/UnhandledExceptionServiceImpl.java @@ -0,0 +1,81 @@ +/******************************************************************************* + * 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> 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 handler) { + handlers.add(handler); + } + + @Override + public synchronized void handle(Throwable t) { + for (Consumer 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); + } + +} diff --git a/bundles/pom.xml b/bundles/pom.xml index e90980faf..d8cdbb346 100644 --- a/bundles/pom.xml +++ b/bundles/pom.xml @@ -177,6 +177,7 @@ org.simantics.msvc.runtime.x86_64 org.simantics.nativemem org.simantics.objmap2 + org.simantics.platform.ui org.simantics.platform.ui.ontology org.simantics.project org.simantics.project.ontology diff --git a/features/org.simantics.ui.workbench.feature/feature.xml b/features/org.simantics.ui.workbench.feature/feature.xml index 06ab0f00e..72baed8b3 100644 --- a/features/org.simantics.ui.workbench.feature/feature.xml +++ b/features/org.simantics.ui.workbench.feature/feature.xml @@ -277,4 +277,11 @@ version="0.0.0" unpack="false"/> + + -- 2.43.2