1 /*******************************************************************************
\r
2 * Copyright (c) 2007, 2010 Association for Decentralized Information Management
\r
3 * in Industry THTH ry.
\r
4 * All rights reserved. This program and the accompanying materials
\r
5 * are made available under the terms of the Eclipse Public License v1.0
\r
6 * which accompanies this distribution, and is available at
\r
7 * http://www.eclipse.org/legal/epl-v10.html
\r
10 * VTT Technical Research Centre of Finland - initial API and implementation
\r
11 *******************************************************************************/
\r
12 package org.simantics.workbench.internal;
\r
14 import java.io.PrintWriter;
\r
15 import java.io.StringWriter;
\r
17 import org.eclipse.jface.dialogs.IDialogConstants;
\r
18 import org.eclipse.jface.dialogs.TrayDialog;
\r
19 import org.eclipse.jface.layout.GridDataFactory;
\r
20 import org.eclipse.swt.SWT;
\r
21 import org.eclipse.swt.widgets.Composite;
\r
22 import org.eclipse.swt.widgets.Control;
\r
23 import org.eclipse.swt.widgets.Display;
\r
24 import org.eclipse.swt.widgets.Label;
\r
25 import org.eclipse.swt.widgets.Shell;
\r
26 import org.eclipse.swt.widgets.Text;
\r
27 import org.eclipse.ui.IWorkbenchWindow;
\r
28 import org.eclipse.ui.PlatformUI;
\r
29 import org.simantics.PlatformException;
\r
30 import org.simantics.SimanticsPlatform;
\r
31 import org.simantics.db.Session;
\r
32 import org.simantics.db.SessionManager;
\r
33 import org.simantics.db.SessionReference;
\r
34 import org.simantics.db.event.SessionEvent;
\r
35 import org.simantics.db.event.SessionListener;
\r
36 import org.simantics.db.management.ISessionContext;
\r
37 import org.simantics.db.management.ISessionContextChangedListener;
\r
38 import org.simantics.db.management.ISessionContextProvider;
\r
39 import org.simantics.db.management.SessionContextChangedEvent;
\r
40 import org.simantics.db.service.LifecycleSupport;
\r
41 import org.simantics.utils.ui.ErrorLogger;
\r
44 * SessionWatchdog is essentially a session manager event listener that catches
\r
45 * exceptions happening within the session connection. Currently the only
\r
46 * procedure to take when a connection exception comes is to just kill the
\r
47 * application since there's really no client-visible way of recovering from the
\r
51 * Note that SessionWatchdog is currently for use with the eclipse Workbench UI.
\r
52 * It is not intended for headless application use.
\r
55 * @author Tuukka Lehtonen
\r
57 public class SessionWatchdog implements SessionListener, ISessionContextChangedListener {
\r
59 SessionManager currentManager;
\r
61 public void attach(ISessionContextProvider scp) {
\r
62 scp.addContextChangedListener(this);
\r
65 private void attach(Session session) {
\r
66 SessionManager sessionManager = null;
\r
67 synchronized (this) {
\r
68 sessionManager = toSessionManager(session);
\r
69 if (sessionManager == currentManager)
\r
73 if (currentManager != null) {
\r
74 currentManager.removeSessionListener(this);
\r
76 this.currentManager = sessionManager;
\r
77 if (sessionManager != null) {
\r
78 sessionManager.addSessionListener(this);
\r
82 private SessionManager toSessionManager(Session s) {
\r
85 LifecycleSupport ls = s.peekService(LifecycleSupport.class);
\r
88 return ls.getSessionManager();
\r
92 public void sessionContextChanged(SessionContextChangedEvent event) {
\r
93 ISessionContext sc = event.getNewValue();
\r
94 attach((sc != null) ? sc.getSession() : (Session) null);
\r
98 public void sessionOpened(SessionEvent e) {
\r
99 //System.err.println(this + ": session opened: " + e.getSession() + "\n" + e.getCause());
\r
103 public void sessionClosed(SessionEvent e) {
\r
104 //System.err.println(this + ": session closed: " + e.getSession() + "\n" + e.getCause());
\r
105 if (e.getCause() != null) {
\r
106 ErrorLogger.defaultLogError(e.getCause());
\r
108 // TODO: attempt reconnection, this is the right place for that.
\r
109 reconnect(e.getSession());
\r
113 private void reconnect(Session deadSession) {
\r
114 // Session s = deadSession;
\r
116 // LifecycleSupport ls = s.getService(LifecycleSupport.class);
\r
117 // SessionReference sr = ls.getSessionReference();
\r
119 // TODO: make it possible to get the old user authenticator agent from the dead session!
\r
120 // SessionUserSupport sus = s.getService(SessionUserSupport.class);
\r
121 // String userName = sus.getUserName();
\r
122 // String localDigest = sus.getUserLocalDigest();
\r
123 // String remoteDigest = sus.getUserRemoteDigest();
\r
124 // currentManager.createSession(sr,
\r
125 // UserAuthenticationAgents.staticAgent(
\r
126 // UserAuthenticators.byNameAndDigest(userName, localDigest, remoteDigest)));
\r
130 public void sessionException(SessionEvent e) {
\r
131 // First things first, log the error.
\r
132 ErrorLogger.defaultLogError(e.getCause());
\r
134 Session s = e.getSession();
\r
135 LifecycleSupport ls = s.getService(LifecycleSupport.class);
\r
136 SessionReference sr = ls.getSessionReference();
\r
138 Display display = Display.getDefault();
\r
139 display.asyncExec(dialogOpener(e.getCause(), sr));
\r
142 private Runnable dialogOpener(final Throwable cause, final SessionReference sr) {
\r
143 return new Runnable() {
\r
145 public void run() {
\r
146 IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
\r
147 if (window == null)
\r
149 Shell shell = window.getShell();
\r
153 DatabaseConnectionProblemDialog d = new DatabaseConnectionProblemDialog(shell, cause, sr);
\r
154 d.setBlockOnOpen(true);
\r
156 // The user chose to continue, but this will with 99.9%
\r
157 // certainty just result in a program that is stuck.
\r
163 private static void shutdown() {
\r
164 // Try to kill all active servers nicely and possibly with force
\r
165 // if nice doesn't work.
\r
167 SimanticsPlatform.INSTANCE.shutdown(null);
\r
168 } catch (PlatformException e) {
\r
169 e.printStackTrace();
\r
172 // The workbench won't close if we try to close it using
\r
173 // PlatformUI.getWorkbench().close() so we'll just kill the
\r
174 // process for now.
\r
178 class DatabaseConnectionProblemDialog extends TrayDialog {
\r
181 SessionReference sr;
\r
183 public DatabaseConnectionProblemDialog(Shell shell, Throwable cause, SessionReference sr) {
\r
185 this.cause = cause;
\r
188 setShellStyle(SWT.TITLE | SWT.APPLICATION_MODAL | SWT.RESIZE | getDefaultOrientation());
\r
192 protected boolean isResizable() {
\r
197 protected void configureShell(Shell newShell) {
\r
198 super.configureShell(newShell);
\r
199 newShell.setSize(700, 600);
\r
200 newShell.setText("Database Connection Problem");
\r
204 protected void createButtonsForButtonBar(Composite parent) {
\r
205 createButton(parent, IDialogConstants.PROCEED_ID, "Continue", false);
\r
206 createButton(parent, IDialogConstants.CLOSE_ID, IDialogConstants.CLOSE_LABEL, true);
\r
210 protected void buttonPressed(int buttonId) {
\r
211 if (IDialogConstants.CLOSE_ID == buttonId) {
\r
213 } else if (IDialogConstants.PROCEED_ID == buttonId) {
\r
214 // Continue == CANCEL.
\r
220 protected Control createDialogArea(Composite parent) {
\r
221 Composite c = (Composite) super.createDialogArea(parent);
\r
223 Text text = new Text(c, SWT.MULTI | SWT.READ_ONLY | SWT.WRAP);
\r
224 StringBuilder sb = new StringBuilder();
\r
225 sb.append("Our apologies, it seems that the database connection to used by the workbench has been unexpectedly disconnected. We are working on these stability problems.");
\r
227 sb.append("Session Reference was: " + sr);
\r
229 sb.append("Unfortunately nothing can be done at this point to recover your unsaved work. The program will be forcibly closed if you select close. You may select continue to attempt recovery but be warned that this will most likely not achieve anything useful.");
\r
231 sb.append("TODO: implement a possibility to attempt reconnection here if the database is still up and running!");
\r
232 text.setText(sb.toString());
\r
233 GridDataFactory.fillDefaults().grab(true, true).applyTo(text);
\r
235 Label causeLabel = new Label(c, SWT.NONE);
\r
236 causeLabel.setText("Exception:");
\r
237 GridDataFactory.fillDefaults().grab(true, false).applyTo(causeLabel);
\r
239 Text causeText = new Text(c, SWT.MULTI | SWT.READ_ONLY | SWT.WRAP | SWT.BORDER);
\r
240 StringWriter sw = new StringWriter();
\r
241 cause.printStackTrace(new PrintWriter(sw));
\r
242 causeText.setText(sw.toString());
\r
243 GridDataFactory.fillDefaults().grab(true, true).applyTo(causeText);
\r