]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.workbench/src/org/simantics/workbench/internal/SessionWatchdog.java
Improve startup time for fresh or rollback'd session in index writing
[simantics/platform.git] / bundles / org.simantics.workbench / src / org / simantics / workbench / internal / SessionWatchdog.java
1 /*******************************************************************************
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management
3  * in Industry THTH ry.
4  * All rights reserved. This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License v1.0
6  * which accompanies this distribution, and is available at
7  * http://www.eclipse.org/legal/epl-v10.html
8  *
9  * Contributors:
10  *     VTT Technical Research Centre of Finland - initial API and implementation
11  *******************************************************************************/
12 package org.simantics.workbench.internal;
13
14 import java.io.PrintWriter;
15 import java.io.StringWriter;
16
17 import org.eclipse.jface.dialogs.IDialogConstants;
18 import org.eclipse.jface.dialogs.TrayDialog;
19 import org.eclipse.jface.layout.GridDataFactory;
20 import org.eclipse.swt.SWT;
21 import org.eclipse.swt.widgets.Composite;
22 import org.eclipse.swt.widgets.Control;
23 import org.eclipse.swt.widgets.Display;
24 import org.eclipse.swt.widgets.Label;
25 import org.eclipse.swt.widgets.Shell;
26 import org.eclipse.swt.widgets.Text;
27 import org.eclipse.ui.IWorkbenchWindow;
28 import org.eclipse.ui.PlatformUI;
29 import org.simantics.PlatformException;
30 import org.simantics.SimanticsPlatform;
31 import org.simantics.db.Session;
32 import org.simantics.db.SessionManager;
33 import org.simantics.db.SessionReference;
34 import org.simantics.db.event.SessionEvent;
35 import org.simantics.db.event.SessionListener;
36 import org.simantics.db.management.ISessionContext;
37 import org.simantics.db.management.ISessionContextChangedListener;
38 import org.simantics.db.management.ISessionContextProvider;
39 import org.simantics.db.management.SessionContextChangedEvent;
40 import org.simantics.db.service.LifecycleSupport;
41 import org.simantics.utils.ui.ErrorLogger;
42
43 /**
44  * SessionWatchdog is essentially a session manager event listener that catches
45  * exceptions happening within the session connection. Currently the only
46  * procedure to take when a connection exception comes is to just kill the
47  * application since there's really no client-visible way of recovering from the
48  * errors.
49  * 
50  * <p>
51  * Note that SessionWatchdog is currently for use with the eclipse Workbench UI.
52  * It is not intended for headless application use.
53  * </p>
54  * 
55  * @author Tuukka Lehtonen
56  */
57 public class SessionWatchdog implements SessionListener, ISessionContextChangedListener {
58
59     SessionManager currentManager;
60
61     public void attach(ISessionContextProvider scp) {
62         scp.addContextChangedListener(this);
63     }
64
65     private void attach(Session session) {
66         SessionManager sessionManager = null;
67         synchronized (this) {
68             sessionManager = toSessionManager(session);
69             if (sessionManager == currentManager)
70                 return;
71         }
72
73         if (currentManager != null) {
74             currentManager.removeSessionListener(this);
75         }
76         this.currentManager = sessionManager;
77         if (sessionManager != null) {
78             sessionManager.addSessionListener(this);
79         }
80     }
81
82     private SessionManager toSessionManager(Session s) {
83         if (s == null)
84             return null;
85         LifecycleSupport ls = s.peekService(LifecycleSupport.class);
86         if (ls == null)
87             return null;
88         return ls.getSessionManager();
89     }
90
91     @Override
92     public void sessionContextChanged(SessionContextChangedEvent event) {
93         ISessionContext sc = event.getNewValue();
94         attach((sc != null) ? sc.getSession() : (Session) null);
95     }
96
97     @Override
98     public void sessionOpened(SessionEvent e) {
99         //System.err.println(this + ": session opened: " + e.getSession() + "\n" + e.getCause());
100     }
101
102     @Override
103     public void sessionClosed(SessionEvent e) {
104         //System.err.println(this + ": session closed: " + e.getSession() + "\n" + e.getCause());
105         if (e.getCause() != null) {
106             ErrorLogger.defaultLogError(e.getCause());
107
108             // TODO: attempt reconnection, this is the right place for that.
109             reconnect(e.getSession());
110         }
111     }
112
113     private void reconnect(Session deadSession) {
114 //        Session s = deadSession;
115 //
116 //        LifecycleSupport ls = s.getService(LifecycleSupport.class);
117 //        SessionReference sr = ls.getSessionReference();
118
119         // TODO: make it possible to get the old user authenticator agent from the dead session!
120 //        SessionUserSupport sus = s.getService(SessionUserSupport.class);
121 //        String userName = sus.getUserName();
122 //        String localDigest = sus.getUserLocalDigest();
123 //        String remoteDigest = sus.getUserRemoteDigest();
124 //        currentManager.createSession(sr,
125 //                UserAuthenticationAgents.staticAgent(
126 //                        UserAuthenticators.byNameAndDigest(userName, localDigest, remoteDigest)));
127     }
128
129     @Override
130     public void sessionException(SessionEvent e) {
131         // First things first, log the error.
132         ErrorLogger.defaultLogError(e.getCause());
133
134         Session s = e.getSession();
135         LifecycleSupport ls = s.getService(LifecycleSupport.class);
136         SessionReference sr = ls.getSessionReference();
137
138         Display display = Display.getDefault();
139         display.asyncExec(dialogOpener(e.getCause(), sr));
140     }
141
142     private Runnable dialogOpener(final Throwable cause, final SessionReference sr) {
143         return new Runnable() {
144             @Override
145             public void run() {
146                 IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
147                 if (window == null)
148                     return;
149                 Shell shell = window.getShell();
150                 if (shell == null)
151                     return;
152
153                 DatabaseConnectionProblemDialog d = new DatabaseConnectionProblemDialog(shell, cause, sr);
154                 d.setBlockOnOpen(true);
155                 d.open();
156                 // The user chose to continue, but this will with 99.9%
157                 // certainty just result in a program that is stuck.
158                 return;
159             }
160         };
161     }
162
163     private static void shutdown() {
164         // Try to kill all active servers nicely and possibly with force
165         // if nice doesn't work.
166         try {
167             SimanticsPlatform.INSTANCE.shutdown(null);
168         } catch (PlatformException e) {
169             e.printStackTrace();
170         }
171
172         // The workbench won't close if we try to close it using
173         // PlatformUI.getWorkbench().close() so we'll just kill the
174         // process for now.
175         System.exit(-2);
176     }
177
178     class DatabaseConnectionProblemDialog extends TrayDialog {
179
180         Throwable cause;
181         SessionReference sr;
182
183         public DatabaseConnectionProblemDialog(Shell shell, Throwable cause, SessionReference sr) {
184             super(shell);
185             this.cause = cause;
186             this.sr = sr;
187
188             setShellStyle(SWT.TITLE | SWT.APPLICATION_MODAL | SWT.RESIZE | getDefaultOrientation());
189         }
190
191         @Override
192         protected boolean isResizable() {
193             return true;
194         }
195
196         @Override
197         protected void configureShell(Shell newShell) {
198             super.configureShell(newShell);
199             newShell.setSize(700, 600);
200             newShell.setText("Database Connection Problem");
201         }
202
203         @Override
204         protected void createButtonsForButtonBar(Composite parent) {
205             createButton(parent, IDialogConstants.PROCEED_ID, "Continue", false);
206             createButton(parent, IDialogConstants.CLOSE_ID, IDialogConstants.CLOSE_LABEL, true);
207         }
208
209         @Override
210         protected void buttonPressed(int buttonId) {
211             if (IDialogConstants.CLOSE_ID == buttonId) {
212                 shutdown();
213             } else if (IDialogConstants.PROCEED_ID == buttonId) {
214                 // Continue == CANCEL.
215                 cancelPressed();
216             }
217         }
218
219         @Override
220         protected Control createDialogArea(Composite parent) {
221             Composite c = (Composite) super.createDialogArea(parent);
222
223             Text text = new Text(c, SWT.MULTI | SWT.READ_ONLY | SWT.WRAP);
224             StringBuilder sb = new StringBuilder();
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.");
226             sb.append("\n\n");
227             sb.append("Session Reference was: " + sr);
228             sb.append("\n\n");
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.");
230             sb.append("\n\n");
231             sb.append("TODO: implement a possibility to attempt reconnection here if the database is still up and running!");
232             text.setText(sb.toString());
233             GridDataFactory.fillDefaults().grab(true, true).applyTo(text);
234
235             Label causeLabel = new Label(c, SWT.NONE);
236             causeLabel.setText("Exception:");
237             GridDataFactory.fillDefaults().grab(true, false).applyTo(causeLabel);
238
239             Text causeText = new Text(c, SWT.MULTI | SWT.READ_ONLY | SWT.WRAP | SWT.BORDER);
240             StringWriter sw = new StringWriter();
241             cause.printStackTrace(new PrintWriter(sw));
242             causeText.setText(sw.toString());
243             GridDataFactory.fillDefaults().grab(true, true).applyTo(causeText);
244
245             return c;
246         }
247
248     }
249
250 }