]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.workbench/src/org/simantics/workbench/internal/SessionWatchdog.java
Include acorn db in db.client feature and make it the default db driver
[simantics/platform.git] / bundles / org.simantics.workbench / src / org / simantics / workbench / internal / SessionWatchdog.java
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
8  *\r
9  * Contributors:\r
10  *     VTT Technical Research Centre of Finland - initial API and implementation\r
11  *******************************************************************************/\r
12 package org.simantics.workbench.internal;\r
13 \r
14 import java.io.PrintWriter;\r
15 import java.io.StringWriter;\r
16 \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
42 \r
43 /**\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
48  * errors.\r
49  * \r
50  * <p>\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
53  * </p>\r
54  * \r
55  * @author Tuukka Lehtonen\r
56  */\r
57 public class SessionWatchdog implements SessionListener, ISessionContextChangedListener {\r
58 \r
59     SessionManager currentManager;\r
60 \r
61     public void attach(ISessionContextProvider scp) {\r
62         scp.addContextChangedListener(this);\r
63     }\r
64 \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
70                 return;\r
71         }\r
72 \r
73         if (currentManager != null) {\r
74             currentManager.removeSessionListener(this);\r
75         }\r
76         this.currentManager = sessionManager;\r
77         if (sessionManager != null) {\r
78             sessionManager.addSessionListener(this);\r
79         }\r
80     }\r
81 \r
82     private SessionManager toSessionManager(Session s) {\r
83         if (s == null)\r
84             return null;\r
85         LifecycleSupport ls = s.peekService(LifecycleSupport.class);\r
86         if (ls == null)\r
87             return null;\r
88         return ls.getSessionManager();\r
89     }\r
90 \r
91     @Override\r
92     public void sessionContextChanged(SessionContextChangedEvent event) {\r
93         ISessionContext sc = event.getNewValue();\r
94         attach((sc != null) ? sc.getSession() : (Session) null);\r
95     }\r
96 \r
97     @Override\r
98     public void sessionOpened(SessionEvent e) {\r
99         //System.err.println(this + ": session opened: " + e.getSession() + "\n" + e.getCause());\r
100     }\r
101 \r
102     @Override\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
107 \r
108             // TODO: attempt reconnection, this is the right place for that.\r
109             reconnect(e.getSession());\r
110         }\r
111     }\r
112 \r
113     private void reconnect(Session deadSession) {\r
114 //        Session s = deadSession;\r
115 //\r
116 //        LifecycleSupport ls = s.getService(LifecycleSupport.class);\r
117 //        SessionReference sr = ls.getSessionReference();\r
118 \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
127     }\r
128 \r
129     @Override\r
130     public void sessionException(SessionEvent e) {\r
131         // First things first, log the error.\r
132         ErrorLogger.defaultLogError(e.getCause());\r
133 \r
134         Session s = e.getSession();\r
135         LifecycleSupport ls = s.getService(LifecycleSupport.class);\r
136         SessionReference sr = ls.getSessionReference();\r
137 \r
138         Display display = Display.getDefault();\r
139         display.asyncExec(dialogOpener(e.getCause(), sr));\r
140     }\r
141 \r
142     private Runnable dialogOpener(final Throwable cause, final SessionReference sr) {\r
143         return new Runnable() {\r
144             @Override\r
145             public void run() {\r
146                 IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();\r
147                 if (window == null)\r
148                     return;\r
149                 Shell shell = window.getShell();\r
150                 if (shell == null)\r
151                     return;\r
152 \r
153                 DatabaseConnectionProblemDialog d = new DatabaseConnectionProblemDialog(shell, cause, sr);\r
154                 d.setBlockOnOpen(true);\r
155                 d.open();\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
158                 return;\r
159             }\r
160         };\r
161     }\r
162 \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
166         try {\r
167             SimanticsPlatform.INSTANCE.shutdown(null);\r
168         } catch (PlatformException e) {\r
169             e.printStackTrace();\r
170         }\r
171 \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
175         System.exit(-2);\r
176     }\r
177 \r
178     class DatabaseConnectionProblemDialog extends TrayDialog {\r
179 \r
180         Throwable cause;\r
181         SessionReference sr;\r
182 \r
183         public DatabaseConnectionProblemDialog(Shell shell, Throwable cause, SessionReference sr) {\r
184             super(shell);\r
185             this.cause = cause;\r
186             this.sr = sr;\r
187 \r
188             setShellStyle(SWT.TITLE | SWT.APPLICATION_MODAL | SWT.RESIZE | getDefaultOrientation());\r
189         }\r
190 \r
191         @Override\r
192         protected boolean isResizable() {\r
193             return true;\r
194         }\r
195 \r
196         @Override\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
201         }\r
202 \r
203         @Override\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
207         }\r
208 \r
209         @Override\r
210         protected void buttonPressed(int buttonId) {\r
211             if (IDialogConstants.CLOSE_ID == buttonId) {\r
212                 shutdown();\r
213             } else if (IDialogConstants.PROCEED_ID == buttonId) {\r
214                 // Continue == CANCEL.\r
215                 cancelPressed();\r
216             }\r
217         }\r
218 \r
219         @Override\r
220         protected Control createDialogArea(Composite parent) {\r
221             Composite c = (Composite) super.createDialogArea(parent);\r
222 \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
226             sb.append("\n\n");\r
227             sb.append("Session Reference was: " + sr);\r
228             sb.append("\n\n");\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
230             sb.append("\n\n");\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
234 \r
235             Label causeLabel = new Label(c, SWT.NONE);\r
236             causeLabel.setText("Exception:");\r
237             GridDataFactory.fillDefaults().grab(true, false).applyTo(causeLabel);\r
238 \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
244 \r
245             return c;\r
246         }\r
247 \r
248     }\r
249 \r
250 }\r