Merge branch 'develop' into termSearch
[taxeditor.git] / eu.etaxonomy.taxeditor.store / src / main / java / eu / etaxonomy / taxeditor / model / MessagingUtils.java
index 1590474e05a2dee7315e99a1aba21c2954178ff3..aa3058954f17f2867755ab943faab0cb75940ed4 100644 (file)
@@ -5,14 +5,20 @@ import java.io.StringWriter;
 import java.util.ArrayList;
 import java.util.List;
 
+import org.apache.commons.lang.exception.ExceptionUtils;
 import org.apache.log4j.Logger;
 import org.eclipse.core.runtime.IStatus;
 import org.eclipse.core.runtime.MultiStatus;
+import org.eclipse.core.runtime.Platform;
 import org.eclipse.core.runtime.Status;
 import org.eclipse.jface.dialogs.MessageDialog;
 import org.eclipse.swt.widgets.Display;
 
-import eu.etaxonomy.cdm.persistence.hibernate.permission.SecurityExceptionUtils;
+import eu.etaxonomy.cdm.common.CdmUtils;
+import eu.etaxonomy.cdm.config.ICdmSource;
+import eu.etaxonomy.cdm.test.integration.SecurityExceptionUtils;
+import eu.etaxonomy.taxeditor.remoting.source.CdmRemoteSource;
+import eu.etaxonomy.taxeditor.store.CdmStore;
 import eu.etaxonomy.taxeditor.store.internal.TaxeditorStorePlugin;
 
 /**
@@ -27,7 +33,11 @@ import eu.etaxonomy.taxeditor.store.internal.TaxeditorStorePlugin;
 public class MessagingUtils {
     public final static String UNEXPECTED_ERROR_MESSAGE = "This is an unexpected error.";
     public final static String CONTACT_MESSAGE = System.getProperty("line.separator") +  "Please contact EDIT Support (EditSupport@bgbm.org) with the error trace below (click on the 'Details' button).";
-
+    public final static String DEFAULT_MESSAGE = "Error thrown but no associated message";
+    public final static String CONNECTION_FAILURE_MESSAGE = "The connection to the remote server has been broken";
+    public final static String REMOTE_ACCESS_FAILURE_MESSAGE = "Maybe the server is currently not available. If the problem persists please contact the server admin with the error trace below.";
+    public static final String WIDGET_IS_DISPOSED_MESSAGE = "A widget was called, which was already disposed";
+    public static final String ACCESS_DENIED = "The connection to the server could not be established because the access was denied.";
     /**
      * Gets the Log4J logger for a given class
      *
@@ -154,61 +164,141 @@ public class MessagingUtils {
         error(source.getClass(), t.getMessage(), t);
     }
 
+
+
+    /**
+     * Returns a list of strings, providing info on,
+     *  - login
+     *  - editor version
+     *  - server (address + source name)
+     *  - db schema version
+     *
+     * @return
+     */
+    public static List<String> getContextInfo() {
+        List<String> contextInfo = new ArrayList<String>();
+        String name = "";
+        String contextPath = "";
+        String schemaVersion = "";
+        String server = "";
+        String version = "";
+        String login = "";
+        try {
+            version = Platform.getBundle("eu.etaxonomy.taxeditor.application").getHeaders().get(org.osgi.framework.Constants.BUNDLE_VERSION);
+
+            ICdmSource activeCdmSource = CdmStore.getActiveCdmSource();
+            if(activeCdmSource != null ) {
+                login = CdmStore.getLoginManager().getAuthenticatedUser().getUsername();
+                name = activeCdmSource.getName();
+                schemaVersion = activeCdmSource.getDbSchemaVersion();
+                server = activeCdmSource.getServer();
+                if(activeCdmSource instanceof CdmRemoteSource){
+                    contextPath = ((CdmRemoteSource) activeCdmSource).getContextPath();
+                    if (contextPath != null && contextPath.startsWith("cdmserver/")){
+                       contextPath = contextPath.substring("cdmserver/".length());
+                    }
+                }
+            }
+
+        } catch (Exception e) {
+            // Nothing to do
+        }
+        contextInfo.add("login : " + login);
+        contextInfo.add("editor version : " + version);
+        contextInfo.add("server : " + server + " (" + name + ")" + (CdmUtils.isNotBlank(contextPath)?" / "+contextPath:""));
+        contextInfo.add("schema version : " + schemaVersion);
+        contextInfo.add("os : " + System.getProperty("os.name")+" "+System.getProperty("os.version")+" "+System.getProperty("os.arch"));
+        contextInfo.add("java : "+System.getProperty("java.version"));
+
+        return contextInfo;
+    }
+
+    public static String getStackTraceAndContextInfo(Throwable t, List<String> contextInfo)  {
+        StringBuffer stackTraceAndContextInfo = new StringBuffer();
+        Throwable throwable = t;
+
+        for(String infoItem : contextInfo) {
+            stackTraceAndContextInfo.append(infoItem + System.getProperty("line.separator"));
+        }
+
+        StringWriter sw = new StringWriter();
+
+        if(throwable == null) {
+            throwable = getDefaultThrowable();
+        }
+        throwable.printStackTrace(new PrintWriter(sw));
+
+        stackTraceAndContextInfo.append(sw.toString());
+
+        return stackTraceAndContextInfo.toString();
+    }
+
+    public static String getContextInfo(List<String> contextInfo)  {
+        StringBuffer scontextInfoStringBuffer = new StringBuffer();
+
+
+        for(String infoItem : contextInfo) {
+            scontextInfoStringBuffer.append(infoItem + System.getProperty("line.separator"));
+        }
+
+
+
+        return scontextInfoStringBuffer.toString();
+    }
+
+    private static Throwable getDefaultThrowable() {
+        return new Throwable("Error thrown but no associated exception");
+    }
+
+
+
     /**
      * Displays a {@link eu.etaxonomy.taxeditor.model.CdmErrorDialog}.
      *
      * @param title
-     *            a {@link java.lang.String} object.
      * @param source
-     *            a {@link java.lang.Object} object.
+     * @param t
+     * @param contextInfo
+     * @param message
      * @param status
-     *            a {@link org.eclipse.core.runtime.IStatus} object.
      */
     private static void errorDialog(final String title,
             final Object source,
+            final Throwable t,
+            final List<String> contextInfo,
             final String message,
-            final IStatus status) {
+            final MultiStatus status,
+            final boolean showReason) {
 
         Display.getDefault().asyncExec(new Runnable() {
 
             @Override
             public void run() {
-                CdmErrorDialog ced = new CdmErrorDialog(AbstractUtility.getShell(), title, message, status);
+                String stackTraceWithContext = getStackTraceAndContextInfo(t, contextInfo);
+                CdmErrorDialog ced = new CdmErrorDialog(AbstractUtility.getShell(), title, message, status, stackTraceWithContext, showReason);
                 ced.open();
                 Class<? extends Object> clazz = source != null ? source.getClass() : this.getClass();
-                error(clazz, status);
-            }
-        });
-    }
-
-    private static void errorDialog(final String title,
-            final Object source,
-            final Throwable t,
-            final MultiStatus status) {
 
-        Display.getDefault().asyncExec(new Runnable() {
 
-            @Override
-            public void run() {
-                CdmErrorDialog ced = new CdmErrorDialog(AbstractUtility.getShell(), title, t.getMessage(), status);
-                ced.open();
-                Class<? extends Object> clazz = source != null ? source.getClass() : this.getClass();
-
-                // Usually the status contains only the first line of the stack trace.
-                // For the unexpected messages we need the entire stack trace so we
-                // create a new status with the entire stacktrace
-                StringWriter sw = new StringWriter();
-                t.printStackTrace(new PrintWriter(sw));
                 IStatus singleStatus = new Status(IStatus.ERROR,
                         status.getPlugin(),
-                        status.getMessage(),
-                        new Exception(sw.toString()));
+                        message,
+                        new Exception(stackTraceWithContext));
 
                 error(clazz, singleStatus);
             }
         });
     }
 
+    public static void errorDialog(final String title,
+            final Object source,
+            final String message,
+            final String pluginId,
+            final Throwable t,
+            boolean addContactMesg) {
+        errorDialog(title, source, message, pluginId, t, addContactMesg, true);
+
+    }
     /**
      * Displays a {@link eu.etaxonomy.taxeditor.model.CdmErrorDialog}.
      *
@@ -223,41 +313,77 @@ public class MessagingUtils {
             final String message,
             final String pluginId,
             final Throwable t,
-            boolean addContactMesg) {
-
+            boolean addContactMesg,
+            boolean showReason) {
 
+        Throwable throwable = t;
         StringBuffer sbStackTrace = new StringBuffer();
 
         // We need to build a MultiStatus object since the simple
         // idea of writing out the stack trace as a single string
         // leads to a single line on windows
         List<Status> childStatuses = new ArrayList<Status>();
-        for (StackTraceElement ste : t.getStackTrace()) {
-            // build & add status
-            childStatuses.add(new Status(IStatus.ERROR, pluginId, "at " + ste.toString()));
+
+        // add context info
+        List<String> contextInfo = getContextInfo();
+        for(String infoItem : contextInfo) {
+            childStatuses.add(new Status(IStatus.ERROR, pluginId, infoItem));
+        }
+
+        if(throwable == null) {
+            throwable = getDefaultThrowable();
         }
 
-        // build message with contact info
+        int thCount = 0;
+        int maxTraces = 4;
+
+        for(Throwable th : ExceptionUtils.getThrowables(throwable)) {
+            // add main exception
+            if(thCount == 0) {
+                for (StackTraceElement ste : th.getStackTrace()) {
+                    childStatuses.add(new Status(IStatus.ERROR, pluginId, "  at " + ste.toString()));
+                }
+            } else {
+                // add recursive causes
+                if(th != null) {
+                    childStatuses.add(new Status(IStatus.ERROR, pluginId, ""));
+                    String msg = th.toString();
+                    childStatuses.add(new Status(IStatus.ERROR, pluginId, "Caused by : " + msg));
+                    int traceCount = 0;
+                    for (StackTraceElement ste : th.getStackTrace()) {
+                        // add only pre-defined number of trace elements
+                        if(traceCount > maxTraces) {
+                            childStatuses.add(new Status(IStatus.ERROR, pluginId, "  ...."));
+                            break;
+                        }
+                        // build & add status
+                        childStatuses.add(new Status(IStatus.ERROR, pluginId, "  at " + ste.toString()));
+                        traceCount++;
+                    }
+                }
+            }
+            thCount++;
+        }
         String finalMessage = message;
 
         if(finalMessage == null || finalMessage.isEmpty()) {
-            finalMessage = "";
+            finalMessage = DEFAULT_MESSAGE;
         }
 
         if(addContactMesg) {
+            // add edit support contact info to message
             finalMessage += MessagingUtils.CONTACT_MESSAGE;
         }
 
         MultiStatus ms = new MultiStatus(pluginId,
                 IStatus.ERROR,
                 childStatuses.toArray(new Status[] {}),
-                finalMessage,
-                t);
+                throwable.toString(),
+                throwable);
 
-        errorDialog(title, source, t, ms);
+        errorDialog(title, source, throwable, contextInfo, finalMessage, ms, showReason);
     }
 
-
     /**
      * Displays a dialog for an exception occurring in an operation.
      *
@@ -298,7 +424,7 @@ public class MessagingUtils {
                     warningDialog(title, source, String.format("You are missing sufficient permissions for the operation \"%s\". %s", operationlabel, hint));
                 } else {
                     title = "Error executing operation";
-                    errorDialog(title, source, String.format("An error occured while executing %s. %s", operationlabel, hint), pluginId, ex, true);
+                    errorDialog(title, source, String.format("An error occurred while executing %s. %s", operationlabel, hint), pluginId, ex, true);
 
                 }
 
@@ -323,6 +449,12 @@ public class MessagingUtils {
         return MessageDialog.openQuestion(AbstractUtility.getShell(), title, message);
     }
 
+    public static int confirmDialog(String title, String message, String...labels){
+        MessageDialog dialog =new MessageDialog(AbstractUtility.getShell(), title, null, message, MessageDialog.QUESTION,labels, 0);
+        int result = dialog.open();
+        return result;
+    }
+
     /**
      * Displays a message {@link org.eclipse.jface.dialogs.MessageDialog}.
      *
@@ -420,6 +552,34 @@ public class MessagingUtils {
         MessagingUtils.warningDialog(title, source, status.getMessage());
     }
 
+    /**
+     * Standard warning dialog for the case when the application is not yet connected to the datasource
+     *
+     * @param source
+     */
+    public static void noDataSourceWarningDialog(Object source) {
+        MessagingUtils
+        .warningDialog(
+                "Application is not connected to a datastore",
+                source,
+                "The requested operation is only available when "
+                + "connected to a datasource. You may choose a datasource to connect to or create a new one in the datasource view.");
+    }
+
+    /**
+     * Standard warning dialog for the case when the datasource is not available
+     *
+     * @param source
+     */
+    public static void dataSourceNotAvailableWarningDialog(Object source) {
+        MessagingUtils
+        .warningDialog(
+                "The datasource is not available",
+                source,
+                "The editor is not connected to a datasource. Maybe the datasource is not available.");
+    }
+
+
     /**
      * Displays a warning {@link org.eclipse.jface.dialogs.MessageDialog}.
      *