Simantics Console 51/3351/7
authorAntti Villberg <antti.villberg@semantum.fi>
Thu, 17 Oct 2019 07:49:30 +0000 (10:49 +0300)
committerTuukka Lehtonen <tuukka.lehtonen@semantum.fi>
Tue, 29 Oct 2019 07:29:32 +0000 (07:29 +0000)
gitlab #400

Change-Id: I18d73b4c9a8d3a4e5b3d3fd43455e3fbf44eceba

17 files changed:
bundles/org.simantics.db.layer0/src/org/simantics/db/layer0/scl/AbstractExpressionCompilationRequest.java
bundles/org.simantics.document.server/src/org/simantics/document/server/request/ServerSCLValueRequest.java
bundles/org.simantics.platform.ui/.classpath [new file with mode: 0644]
bundles/org.simantics.platform.ui/.project [new file with mode: 0644]
bundles/org.simantics.platform.ui/META-INF/MANIFEST.MF [new file with mode: 0644]
bundles/org.simantics.platform.ui/build.properties [new file with mode: 0644]
bundles/org.simantics.platform.ui/src/org/simantics/platform/ui/SimanticsConsole.java [new file with mode: 0644]
bundles/org.simantics.platform.ui/src/org/simantics/platform/ui/internal/Activator.java [new file with mode: 0644]
bundles/org.simantics.platform.ui/src/org/simantics/platform/ui/internal/ConsoleAppender.java [new file with mode: 0644]
bundles/org.simantics.structural2/src/org/simantics/structural2/scl/AbstractCompileStructuralValueRequest.java
bundles/org.simantics.structural2/src/org/simantics/structural2/scl/CompileStructuralValueRequest.java
bundles/org.simantics.workbench/src/org/simantics/workbench/internal/Activator.java
bundles/org.simantics.workbench/src/org/simantics/workbench/internal/SimanticsWorkbenchAdvisor.java
bundles/org.simantics/src/org/simantics/UnhandledExceptionService.java [new file with mode: 0644]
bundles/org.simantics/src/org/simantics/internal/UnhandledExceptionServiceImpl.java [new file with mode: 0644]
bundles/pom.xml
features/org.simantics.ui.workbench.feature/feature.xml

index 3fcdf66ab042679ff7f0fde8e0c1cf520b8d8037..8d364c737bdc5dc3070b59ccbb412a3ad73ff5ca 100644 (file)
@@ -147,6 +147,13 @@ implements Read<Function1<EvaluationContext,Object>> {
     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 {
@@ -160,18 +167,20 @@ implements Read<Function1<EvaluationContext,Object>> {
             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);
index dce4b34ab44e46c7d28ef70b3902b0663ea4fbe6..adff8dab5b9ec87d1271e1ca02d1d3bea58e49a3 100644 (file)
@@ -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<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 {
 
diff --git a/bundles/org.simantics.platform.ui/.classpath b/bundles/org.simantics.platform.ui/.classpath
new file mode 100644 (file)
index 0000000..b862a29
--- /dev/null
@@ -0,0 +1,7 @@
+<?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
diff --git a/bundles/org.simantics.platform.ui/.project b/bundles/org.simantics.platform.ui/.project
new file mode 100644 (file)
index 0000000..f850978
--- /dev/null
@@ -0,0 +1,28 @@
+<?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
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 (file)
index 0000000..f0e776e
--- /dev/null
@@ -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 (file)
index 0000000..41eb6ad
--- /dev/null
@@ -0,0 +1,4 @@
+source.. = src/\r
+output.. = bin/\r
+bin.includes = META-INF/,\\r
+               .\r
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 (file)
index 0000000..c7aa2e1
--- /dev/null
@@ -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\\[(?<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
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 (file)
index 0000000..50c4ff5
--- /dev/null
@@ -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 (file)
index 0000000..8c3bf37
--- /dev/null
@@ -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<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
index c4b4e341fb85b2aa551612e028a2dafca387f06c..fce1abaa2d8a62fa3fcadfd97c12c66b037e0655 100644 (file)
@@ -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<CompilationContext, Object> {
     
+    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);
+    }
+    
 }
index 5e564cc9114943df427ee1a1bda6877926f335ef..ce824090dce3f686af4a37d77096218f4e9e6f8b 100644 (file)
@@ -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);
+       }
+       
 }
index db6cdac8005aa0bc5de354010e2222b34721988c..95ab9929935addc7a3a5c9ed8fa09fa00b6bcf22 100644 (file)
@@ -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);
 
index cb1ee16d315c28d197016d491130a77d38c98e1c..f5bda18b0a137816c209b12972bfeb346605eb2e 100644 (file)
@@ -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 (file)
index 0000000..d094851
--- /dev/null
@@ -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<Throwable> 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 (file)
index 0000000..4276dc9
--- /dev/null
@@ -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<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);
+    }
+
+}
index e90980faf6073dcd02400f55fdf818752d91ba45..d8cdbb34652550b203c9833c377fd4baa369043d 100644 (file)
                <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>
index 06ab0f00edebc78a556e7a14b527ba394af3a4ac..72baed8b3f4f57d9413fd20002ef602263adc31e 100644 (file)
          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>