com.ebmwebsourcing.petals.server.PetalsServerBehavior.java Source code

Java tutorial

Introduction

Here is the source code for com.ebmwebsourcing.petals.server.PetalsServerBehavior.java

Source

/******************************************************************************
 * 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;
    }
}