Java tutorial
/****************************************************************************** * Copyright (c) 2009-2013, Linagora * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Linagora - initial API and implementation *******************************************************************************/ package com.ebmwebsourcing.petals.server; import java.io.File; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Path; import org.eclipse.debug.core.DebugEvent; import org.eclipse.debug.core.DebugException; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.IDebugEventSetListener; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; import org.eclipse.debug.core.model.IProcess; import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants; import org.eclipse.jdt.launching.IRuntimeClasspathEntry; import org.eclipse.jdt.launching.JavaRuntime; import org.eclipse.jface.dialogs.MessageDialogWithToggle; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; import org.eclipse.wst.server.core.IServer; import org.eclipse.wst.server.core.model.ServerBehaviourDelegate; import org.ow2.petals.kernel.ws.api.PEtALSWebServiceException; import org.ow2.petals.kernel.ws.client.PetalsClient; import org.ow2.petals.kernel.ws.client.PetalsClientFactory; import com.ebmwebsourcing.petals.server.runtime.PetalsRuntime; import com.ebmwebsourcing.petals.server.server.PetalsServer; import com.ebmwebsourcing.petals.server.ui.preferences.PetalsServerPreferencePage; /** * The server behavior is in charge of maintaining the state of the server. * <p> * It handles, among other things, the stop and shutdown command of Petals * servers. Initially, it listens to the server by checking the system process and by * pinging the server at regular intervals. * </p> * * @author Vincent Zurczak - EBM WebSourcing */ public class PetalsServerBehavior extends ServerBehaviourDelegate { private IDebugEventSetListener processListener; private boolean keepOnPinging; private Thread pingThread; /** * @return the Petals runtime */ public PetalsRuntime getPetalsRuntime() { PetalsRuntime petalsRuntime = null; if (getServer().getRuntime() != null) petalsRuntime = (PetalsRuntime) getServer().getRuntime().loadAdapter(PetalsRuntime.class, null); return petalsRuntime; } /** * @return the Petals server */ public PetalsServer getPetalsServer() { PetalsServer server = (PetalsServer) getServer().loadAdapter(PetalsServer.class, null); return server; } /** * @return the name of the class to use to start the server in a JVM */ public String getRuntimeClass() { return getPetalsServer().getPetalsRuntimeClass(); } /* * (non-Javadoc) * @see org.eclipse.wst.server.core.model.ServerBehaviourDelegate * #initialize(org.eclipse.core.runtime.IProgressMonitor) */ @Override protected void initialize(IProgressMonitor monitor) { super.initialize(monitor); // Get the server initial state boolean running = getPetalsServer().isRunning(); if (running) setServerState(IServer.STATE_STARTED); else setServerState(IServer.STATE_STOPPED); } /* * (non-Javadoc) * @see org.eclipse.wst.server.core.model.ServerBehaviourDelegate#dispose() */ @Override public void dispose() { stop(false, true); super.dispose(); } /** * Starts listening the system process and pinging the server. * @param newProcess */ public void startListening(final IProcess newProcess) { // Listen to the system process if (this.processListener != null || newProcess == null || this.pingThread != null) return; this.processListener = new IDebugEventSetListener() { @Override public void handleDebugEvents(DebugEvent[] events) { if (events != null) { int size = events.length; for (int i = 0; i < size; i++) { if (newProcess != null && newProcess.equals(events[i].getSource()) && events[i].getKind() == DebugEvent.TERMINATE) { // Atomic section synchronized (this) { stopListening(); setServerState(IServer.STATE_STOPPED); } } } } } }; DebugPlugin.getDefault().addDebugEventListener(this.processListener); // Ping the server while it is starting final Thread startingThread = new Thread() { @Override public void run() { // Ping every second until timeout is over for (int i = 0; i < getServer().getStartTimeout(); i++) { try { Thread.sleep(1000); } catch (InterruptedException e) { // nothing } // Update the state boolean running = getPetalsServer().isRunning(); // Atomic section synchronized (this) { if (running) { setServerState(IServer.STATE_STARTED); break; } } } // If the server is still not started, stop listening // No need to make this part atomic. The other thread is not started. if (!getPetalsServer().isRunning()) { stopListening(); setServerState(IServer.STATE_STOPPED); } }; }; startingThread.start(); // Ping the server to check its state setKeepOnPinging(true); this.pingThread = new Thread() { @Override public void run() { // Wait the starting thread to finish try { startingThread.join(); } catch (InterruptedException e1) { return; } // Ping every 10 seconds while (keepOnPinging()) { try { Thread.sleep(10000); } catch (InterruptedException e) { // nothing } // Update the state boolean running = getPetalsServer().isRunning(); // Atomic section synchronized (this) { if (keepOnPinging()) { if (running) setServerState(IServer.STATE_STARTED); else setServerState(IServer.STATE_STOPPED); } } } } }; this.pingThread.setDaemon(true); this.pingThread.start(); } /** * Stops listening the system process and pinging the server. */ private void stopListening() { if (this.pingThread != null) { setKeepOnPinging(false); this.pingThread = null; } if (this.processListener != null) { DebugPlugin.getDefault().removeDebugEventListener(this.processListener); this.processListener = null; } } /** * Stops the server. * @param force true to stop the process if the normal stop command fails * @see org.eclipse.wst.server.core.model.ServerBehaviourDelegate * #stop(boolean) */ @Override public void stop(boolean force) { stop(false, force); } /** * Shutdowns the server. * <p> * Shutdown means all the deployed artifacts will be removed. * </p> * * @param eraseAll true to erase all the deployed artifacts before stopping the container * @param force true to stop the process if the normal shutdown command fails */ public void stop(boolean eraseAll, boolean force) { int serverState = getServer().getServerState(); if (serverState == IServer.STATE_STOPPED) return; // Atomic section synchronized (this) { stopListening(); setServerState(IServer.STATE_STOPPING); } // Stop with web services boolean normalShutdownFailed = false; String serverAddress = getPetalsServer().getWsUrlAsString(); try { if (getPetalsServer().isRunning()) { PetalsClient client = PetalsClientFactory.getInstance().getClient(serverAddress); if (eraseAll) client.getRuntimeService().shutdownContainer(); else client.getRuntimeService().stopContainer(); } } catch (PEtALSWebServiceException e) { normalShutdownFailed = true; } // Handle errors and forced stop if (normalShutdownFailed && force) terminateProcess(); setServerState(IServer.STATE_STOPPED); } /** * Terminates the launch process. */ private void terminateProcess() { try { ILaunch launch = getServer().getLaunch(); if (launch != null) launch.terminate(); } catch (DebugException e) { PetalsServerPlugin.log(e, IStatus.ERROR); } } /** * Prepares the server to be launched, checking the runtime and cleaning the install directory. * * @param launch * @param launchMode * @param monitor * @throws CoreException */ public void setupLaunch(ILaunch launch, String launchMode, IProgressMonitor monitor) throws CoreException { // Clean the server install directory if (getPetalsServer().isRunning()) setServerState(IServer.STATE_STARTED); int serverState = getServer().getServerState(); if (serverState == IServer.STATE_STARTED || serverState == IServer.STATE_STARTING) return; if (getPetalsServer().isServerInstallationDirty()) { IStatus status = getPetalsServer().cleanServerInstallation(); if (status != null && status.getSeverity() == IStatus.ERROR) throw new CoreException(status); } // Check the runtime (may have been edited) IStatus status = getPetalsRuntime().validate(); if (status != null && status.getSeverity() == IStatus.ERROR) throw new CoreException(status); // Update the server state setServerRestartState(false); setServerState(IServer.STATE_STARTING); setMode(launchMode); } /** * Adds the server libraries in the launch class path. * @see org.eclipse.wst.server.core.model.ServerBehaviourDelegate * #setupLaunchConfiguration(org.eclipse.debug.core.ILaunchConfigurationWorkingCopy, org.eclipse.core.runtime.IProgressMonitor) */ @Override public void setupLaunchConfiguration(ILaunchConfigurationWorkingCopy workingCopy, IProgressMonitor monitor) throws CoreException { // Add the server libraries in the class path List<String> classpath = new ArrayList<String>(); for (Object o : workingCopy.getAttribute(IJavaLaunchConfigurationConstants.ATTR_CLASSPATH, Collections.emptyList())) { if (o instanceof String) classpath.add((String) o); } List<File> serverLibs = getPetalsServer().getPetalsServerLibraries(); for (File serverLib : serverLibs) { Path path = new Path(serverLib.getAbsolutePath()); IRuntimeClasspathEntry entry = JavaRuntime.newArchiveRuntimeClasspathEntry(path); entry.setClasspathProperty(IRuntimeClasspathEntry.USER_CLASSES); classpath.add(entry.getMemento()); } // Update and force the use of this class path in the launch configuration workingCopy.setAttribute(IJavaLaunchConfigurationConstants.ATTR_CLASSPATH, classpath); workingCopy.setAttribute(IJavaLaunchConfigurationConstants.ATTR_DEFAULT_CLASSPATH, false); // Get the server arguments String args = null; final IPreferenceStore store = PetalsServerPlugin.getDefault().getPreferenceStore(); if (store.contains(PetalsServerPreferencePage.START_IN_CONSOLE_MODE)) { String startMode = store.getString(PetalsServerPreferencePage.START_IN_CONSOLE_MODE); if (MessageDialogWithToggle.ALWAYS.equals(startMode)) args = "start -console"; else if (MessageDialogWithToggle.NEVER.equals(startMode)) args = "start"; } if (args == null) { Display.getDefault().syncExec(new Runnable() { @Override public void run() { MessageDialogWithToggle.openYesNoQuestion(new Shell(), "Start Mode", "Do you want to start in console mode?", "Do not ask again", false, store, PetalsServerPreferencePage.START_IN_CONSOLE_MODE); }; }); String startMode = store.getString(PetalsServerPreferencePage.START_IN_CONSOLE_MODE); if (MessageDialogWithToggle.ALWAYS.equals(startMode)) args = "start -console"; else args = "start"; } workingCopy.setAttribute(IJavaLaunchConfigurationConstants.ATTR_PROGRAM_ARGUMENTS, args); } /** * Must be called when the server launch fails. */ public void signalLaunchFailed() { setServerState(IServer.STATE_STOPPED); } /** * @return the keepOnPinging */ public synchronized boolean keepOnPinging() { return this.keepOnPinging; } /** * @param keepOnPinging the keepOnPinging to set */ public synchronized void setKeepOnPinging(boolean keepOnPinging) { this.keepOnPinging = keepOnPinging; } }