001: /*
002: * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright
003: * notice. All rights reserved.
004: */
005: package org.terracotta.dso;
006:
007: import org.eclipse.core.resources.IProject;
008: import org.eclipse.core.runtime.CoreException;
009: import org.eclipse.core.runtime.IProgressMonitor;
010: import org.eclipse.core.runtime.QualifiedName;
011: import org.eclipse.debug.core.DebugEvent;
012: import org.eclipse.debug.core.DebugException;
013: import org.eclipse.debug.core.DebugPlugin;
014: import org.eclipse.debug.core.IDebugEventSetListener;
015: import org.eclipse.debug.core.ILaunch;
016: import org.eclipse.debug.core.model.IProcess;
017: import org.eclipse.jdt.core.IJavaProject;
018: import org.eclipse.jface.operation.IRunnableWithProgress;
019: import org.eclipse.jface.window.ApplicationWindow;
020: import org.eclipse.swt.widgets.Display;
021: import org.eclipse.swt.widgets.Shell;
022: import org.eclipse.ui.IWorkbenchWindow;
023: import org.eclipse.ui.PlatformUI;
024: import org.terracotta.dso.actions.ActionUtil;
025: import org.terracotta.dso.decorator.ServerRunningDecorator;
026: import org.terracotta.dso.dialogs.AbstractApplicationEventDialog;
027: import org.terracotta.dso.dialogs.NonPortableObjectDialog;
028: import org.terracotta.dso.dialogs.ReadOnlyObjectDialog;
029: import org.terracotta.dso.dialogs.UnlockedSharedObjectDialog;
030:
031: import com.tc.admin.ConnectionContext;
032: import com.tc.admin.ConnectionListener;
033: import com.tc.admin.ServerConnectionManager;
034: import com.tc.admin.TCStop;
035: import com.tc.management.beans.L2MBeanNames;
036: import com.tc.object.appevent.AbstractApplicationEvent;
037: import com.tc.object.appevent.NonPortableObjectEvent;
038: import com.tc.object.appevent.ReadOnlyObjectEvent;
039: import com.tc.object.appevent.UnlockedSharedObjectEvent;
040: import com.tc.util.concurrent.ThreadUtil;
041:
042: import java.io.IOException;
043: import java.lang.reflect.InvocationTargetException;
044: import java.util.HashMap;
045: import java.util.Iterator;
046:
047: import javax.management.Notification;
048: import javax.management.NotificationListener;
049: import javax.management.ObjectName;
050:
051: /**
052: * Used to start a server using the project's config information. Listens for user-initiated termination requests and
053: * shuts down the associated server. A mapping is maintained (m_server) between the IProcess and a ServerInfo.
054: * ServerInfo contains the IJavaProject and the JMX port of the running server. The JMX port is used for stopping the
055: * server and it's done this way to because otherwise we must rely on the configuration, which the user can change.
056: *
057: * @see TcPlugin.launchServer
058: */
059:
060: public class ServerTracker implements IDebugEventSetListener {
061: private static ServerTracker m_instance = new ServerTracker();
062: private HashMap<IProcess, ServerInfo> m_servers = new HashMap<IProcess, ServerInfo>();
063:
064: /**
065: * This name is used in plugin.xml for managing the start/stop menu items.
066: */
067: private static final QualifiedName SERVER_RUNNING_NAME = new QualifiedName(
068: "org.terracotta.dso", "ServerRunning");
069:
070: private ServerTracker() {
071: super ();
072: }
073:
074: public static synchronized ServerTracker getDefault() {
075: if (m_instance == null) {
076: m_instance = new ServerTracker();
077: }
078: return m_instance;
079: }
080:
081: public void handleDebugEvents(DebugEvent[] events) {
082: if (events != null && events.length > 0) {
083: if (events[0].getKind() == DebugEvent.TERMINATE) {
084: Object source = events[0].getSource();
085:
086: if (source instanceof IProcess) {
087: ServerInfo serverInfo = m_servers.get(source);
088:
089: if (serverInfo != null) {
090: m_servers.remove(source);
091: serverInfo.setStatus(ServerInfo.TERMINATED);
092: displayStatus("Terracotta Server '"
093: + serverInfo.getName()
094: + "' terminated.");
095: IJavaProject javaProj = serverInfo
096: .getJavaProject();
097: if (!anyRunning(javaProj)) {
098: setRunning(javaProj, null);
099: }
100: }
101: }
102: }
103: }
104: }
105:
106: public boolean anyRunning(IJavaProject javaProj) {
107: Iterator<IProcess> iter = m_servers.keySet().iterator();
108:
109: while (iter.hasNext()) {
110: IProcess proc = iter.next();
111: ServerInfo serverInfo = m_servers.get(proc);
112: IJavaProject project = serverInfo.getJavaProject();
113:
114: if (project.equals(javaProj)) {
115: return true;
116: }
117: }
118:
119: return false;
120: }
121:
122: public ServerInfo getServerInfo(IJavaProject javaProj, String name) {
123: Iterator<IProcess> iter = m_servers.keySet().iterator();
124:
125: while (iter.hasNext()) {
126: IProcess proc = iter.next();
127: ServerInfo serverInfo = m_servers.get(proc);
128: String serverName = serverInfo.getName();
129:
130: if (name.equals(serverName)) {
131: return serverInfo;
132: }
133: }
134:
135: return null;
136: }
137:
138: public boolean isRunning(IJavaProject javaProj, String name) {
139: ServerInfo serverInfo = getServerInfo(javaProj, name);
140: return serverInfo != null && serverInfo.isStarted();
141: }
142:
143: public void setRunning(IJavaProject javaProj, Boolean value) {
144: if (value != null && value.equals(Boolean.FALSE)) {
145: value = null;
146: }
147:
148: IProject project = javaProj.getProject();
149:
150: if (project.isOpen()) {
151: try {
152: project.setSessionProperty(SERVER_RUNNING_NAME, value);
153: ServerRunningDecorator.updateDecorators();
154: } catch (CoreException ce) {/**/
155: }
156: }
157: }
158:
159: public void startServer(IJavaProject javaProject, String name,
160: IProgressMonitor monitor) throws InvocationTargetException {
161: if (isRunning(javaProject, name)) {
162: internalStopServer(javaProject, true, name, monitor);
163: } else {
164: try {
165: internalStartServer(javaProject, name, monitor);
166: } catch (CoreException ce) {
167: throw new InvocationTargetException(ce);
168: }
169: }
170: }
171:
172: private void internalStartServer(final IJavaProject javaProject,
173: final String name, final IProgressMonitor monitor)
174: throws CoreException {
175: monitor.beginTask("Starting Terracotta Server '" + name
176: + "' ...", IProgressMonitor.UNKNOWN);
177:
178: TcPlugin plugin = TcPlugin.getDefault();
179: String projName = javaProject.getElementName();
180: int jmxPort = plugin.getJmxPort(javaProject.getProject(), name);
181: ILaunch launch = plugin.launchServer(javaProject, projName,
182: name, null);
183: String statusMsg = null;
184:
185: if (launch != null) {
186: ServerInfo info = new ServerInfo(javaProject, name,
187: jmxPort != 0 ? jmxPort : 9520);
188: IProcess[] processes = launch.getProcesses();
189:
190: if (processes.length > 0) {
191: m_servers.put(processes[0], info);
192:
193: DebugPlugin.getDefault().addDebugEventListener(this );
194:
195: waitForMBean(javaProject, name, jmxPort > 0 ? jmxPort
196: : 9520);
197: while (!info.isTerminated() && info.isStarting()) {
198: ThreadUtil.reallySleep(1000);
199: }
200: if (info.isTerminated()) {
201: statusMsg = "Terracotta Server '" + name
202: + "' failed to start.";
203: } else if (info.isStarted()) {
204: statusMsg = "Terracotta Server '" + name
205: + "' started.";
206: }
207: }
208: }
209: monitor.done();
210:
211: if (statusMsg != null) {
212: displayStatus(statusMsg);
213: }
214: }
215:
216: private void displayStatus(final String msg) {
217: Display.getDefault().syncExec(new Runnable() {
218: public void run() {
219: IWorkbenchWindow window = PlatformUI.getWorkbench()
220: .getActiveWorkbenchWindow();
221: if (window instanceof ApplicationWindow) {
222: ((ApplicationWindow) window).setStatus(msg);
223: }
224: }
225: });
226: }
227:
228: class L2ConnectListener implements ConnectionListener {
229: IJavaProject fJavaProject;
230: String fName;
231: ServerInfo fServerInfo;
232: ServerConnectionManager fServerConnectionManager;
233:
234: L2ConnectListener(IJavaProject javaProject, final String name) {
235: fJavaProject = javaProject;
236: fName = name;
237: fServerInfo = getServerInfo(fJavaProject, fName);
238: }
239:
240: void setServerConnectionManager(
241: ServerConnectionManager serverConnectionManager) {
242: fServerConnectionManager = serverConnectionManager;
243: }
244:
245: public void handleConnection() {
246: if (fServerConnectionManager == null) {
247: return;
248: }
249:
250: if (fServerInfo.isTerminated()) {
251: stopListening();
252: return;
253: }
254:
255: try {
256: if (fServerConnectionManager.testIsConnected()) {
257: if (fServerConnectionManager.canShutdown()) {
258: fServerInfo.setStatus(ServerInfo.STARTED);
259: setRunning(fJavaProject, Boolean.TRUE);
260:
261: ConnectionContext cc = fServerConnectionManager
262: .getConnectionContext();
263: while (true) {
264: ObjectName on = cc
265: .queryName(L2MBeanNames.DSO_APP_EVENTS
266: .getCanonicalName());
267: if (on != null) {
268: cc.addNotificationListener(on,
269: new DSOAppEventListener());
270: break;
271: }
272: ThreadUtil.reallySleep(250);
273: }
274: }
275: }
276: } catch (Exception e) {
277: e.printStackTrace();
278: }
279: }
280:
281: public void handleException() {
282: if (fServerInfo.isTerminated()) {
283: stopListening();
284: }
285: }
286:
287: private void stopListening() {
288: if (fServerConnectionManager == null) {
289: return;
290: }
291: fServerConnectionManager.tearDown();
292: fServerConnectionManager = null;
293: }
294: }
295:
296: class DSOAppEventListener implements NotificationListener {
297: private boolean fHandlingApplicationEvent;
298:
299: public void handleNotification(Notification notification,
300: Object handback) {
301: final Object event = notification.getSource();
302:
303: if (!fHandlingApplicationEvent) {
304: fHandlingApplicationEvent = true;
305: if (event instanceof AbstractApplicationEvent) {
306: Display.getDefault().asyncExec(new Runnable() {
307: public void run() {
308: try {
309: handleApplicationEvent((AbstractApplicationEvent) event);
310: } catch (Throwable t) {
311: t.printStackTrace();
312: } finally {
313: fHandlingApplicationEvent = false;
314: }
315: }
316: });
317: }
318: }
319: }
320: }
321:
322: private void handleApplicationEvent(AbstractApplicationEvent event) {
323: Shell shell = ActionUtil.findSelectedEditorPart().getSite()
324: .getShell();
325: AbstractApplicationEventDialog dialog = null;
326:
327: if (event instanceof NonPortableObjectEvent) {
328: dialog = new NonPortableObjectDialog(shell,
329: (NonPortableObjectEvent) event);
330: } else if (event instanceof UnlockedSharedObjectEvent) {
331: dialog = new UnlockedSharedObjectDialog(shell,
332: (UnlockedSharedObjectEvent) event);
333: } else if (event instanceof ReadOnlyObjectEvent) {
334: dialog = new ReadOnlyObjectDialog(shell,
335: (ReadOnlyObjectEvent) event);
336: }
337:
338: if (dialog != null) {
339: dialog.open();
340: }
341: }
342:
343: private void waitForMBean(final IJavaProject javaProject,
344: final String name, final int jmxPort) {
345: L2ConnectListener connectListener = new L2ConnectListener(
346: javaProject, name);
347: ServerConnectionManager connectManager = new ServerConnectionManager(
348: "localhost", jmxPort, false, connectListener);
349: connectListener.setServerConnectionManager(connectManager);
350: connectManager.setAutoConnect(true);
351: }
352:
353: public void cancelServer(IJavaProject javaProject) {
354: Iterator<IProcess> iter = m_servers.keySet().iterator();
355: IProcess proc;
356: ServerInfo info;
357:
358: while (iter.hasNext()) {
359: proc = iter.next();
360: info = m_servers.get(proc);
361:
362: if (info.getJavaProject().equals(javaProject)) {
363: try {
364: proc.terminate();
365: } catch (DebugException de) {
366: iter.remove();
367: }
368: }
369: }
370: }
371:
372: public void stopServer(IJavaProject javaProject,
373: IProgressMonitor monitor) throws InvocationTargetException {
374: internalStopServer(javaProject, false, null, monitor);
375: }
376:
377: public void stopServer(IJavaProject javaProject, String name,
378: IProgressMonitor monitor) throws InvocationTargetException {
379: internalStopServer(javaProject, false, name, monitor);
380: }
381:
382: private void internalStopServer(IJavaProject javaProject,
383: boolean restart, String name, IProgressMonitor monitor)
384: throws InvocationTargetException {
385: new TCStopper(javaProject, restart, name).run(monitor);
386: }
387:
388: class TCStopper implements IRunnableWithProgress {
389: IJavaProject m_javaProject;
390: boolean m_restart;
391: String m_name;
392:
393: TCStopper(IJavaProject javaProject) {
394: this (javaProject, false, null);
395: }
396:
397: TCStopper(IJavaProject javaProject, boolean restart, String name) {
398: m_javaProject = javaProject;
399: m_restart = restart;
400: m_name = name;
401: }
402:
403: public void run(IProgressMonitor monitor)
404: throws InvocationTargetException {
405: try {
406: monitor.beginTask("Stopping Terracotta Server '"
407: + m_name + "' ...", IProgressMonitor.UNKNOWN);
408: doStopServer(m_javaProject, m_name, monitor);
409: if (m_restart) {
410: internalStartServer(m_javaProject, m_name, monitor);
411: }
412: } catch (Exception e) {
413: throw new InvocationTargetException(e);
414: }
415: }
416: }
417:
418: private void doStopServer(IJavaProject targetProj,
419: String targetName, IProgressMonitor monitor)
420: throws IOException, DebugException {
421: Iterator<IProcess> iter = m_servers.keySet().iterator();
422: IProcess proc;
423: ServerInfo serverInfo;
424: IJavaProject javaProject;
425: int jmxPort;
426: String name;
427:
428: while (iter.hasNext()) {
429: proc = iter.next();
430: serverInfo = m_servers.get(proc);
431:
432: if (serverInfo.isTerminated()) {
433: monitor.done();
434: return;
435: }
436:
437: javaProject = serverInfo.getJavaProject();
438: jmxPort = serverInfo.getJmxPort();
439: name = serverInfo.getName();
440:
441: if (javaProject.getProject().isOpen()
442: && targetProj.equals(javaProject)
443: && targetName.equals(name)) {
444: TCStop stopper = new TCStop("localhost",
445: jmxPort != -1 ? jmxPort : 9520);
446:
447: stopper.stop();
448:
449: int count = 0;
450: while (true) {
451: try {
452: proc.getExitValue();
453: monitor.done();
454: return;
455: } catch (DebugException de) {
456: if (count++ == 6) {
457: proc.terminate();
458: monitor.done();
459: return;
460: }
461: ThreadUtil.reallySleep(1000);
462: }
463: }
464: }
465: }
466: }
467:
468: public void shutdownAllServers() {
469: Iterator<ServerInfo> iter = m_servers.values().iterator();
470: ServerInfo info;
471:
472: while (iter.hasNext()) {
473: info = iter.next();
474: cancelServer(info.getJavaProject());
475: }
476: }
477: }
|