DefaultServer.java

package org.andromda.core.server;

import java.io.DataInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketTimeoutException;
import org.andromda.core.common.AndroMDALogger;
import org.andromda.core.configuration.Configuration;
import org.andromda.core.engine.Engine;

/**
 * The default AndroMDA {@link Server instance}.
 *
 * @author Chad Brandon
 */
public class DefaultServer
    implements Server
{
    /**
     * The message sent to the client when AndroMDA processing has completed.
     */
    private static final String COMPLETE = "complete";

    /**
     * The command sent to the server that indicates it
     * should stop.
     */
    static final String STOP = "stop";

    /**
     * The server listener.
     */
    private ServerSocket listener = null;

    /**
     * The AndroMDA Engine instance.
     */
    private Engine engine = Engine.newInstance();

    /**
     * @see org.andromda.core.server.Server#start(org.andromda.core.configuration.Configuration)
     */
    public void start(final Configuration configuration)
    {
        engine.initialize(configuration);
        if (configuration != null)
        {
            final org.andromda.core.configuration.Server serverConfiguration = configuration.getServer();
            if (serverConfiguration != null)
            {
                try
                {
                    try
                    {
                        this.listener = new ServerSocket(serverConfiguration.getPort());
                        final int modelLoadInterval = serverConfiguration.getLoadInterval();
                        if (modelLoadInterval > 0)
                        {
                            this.listener.setSoTimeout(serverConfiguration.getLoadInterval());
                        }
                    }
                    catch (final IOException exception)
                    {
                        throw new ServerException(
                            "Could not listen on port '" + serverConfiguration.getPort() +
                            "', change the port in your configuration");
                    }
                    while (true)
                    {
                        try
                        {
                            final Socket client = this.listener.accept();
                            if (client != null)
                            {
                                final ObjectOutputStream serverOutput =
                                    new ObjectOutputStream(client.getOutputStream());
                                final ObjectInputStream objectInput =
                                    new ObjectInputStream(new DataInputStream(client.getInputStream()));
                                try
                                {
                                    final Object object = objectInput.readObject();
                                    if (object instanceof Configuration)
                                    {
                                        this.engine.run((Configuration)object, false, null);
                                    }
                                    else if (object instanceof String)
                                    {
                                        final String string = (String)object;
                                        if (string.equals(STOP))
                                        {
                                            break;
                                        }
                                    }
                                }
                                catch (final Throwable throwable)
                                {
                                    AndroMDALogger.error(throwable);

                                    // - pass the exception to the client
                                    serverOutput.writeObject(throwable);
                                }

                                // - signal to the client, it can stop waiting
                                serverOutput.writeObject(COMPLETE);
                                serverOutput.flush();
                                serverOutput.close();
                                objectInput.close();
                                client.close();
                            }
                        }
                        catch (final SocketTimeoutException exception)
                        {
                            try
                            {
                                this.engine.loadModelsIfNecessary(configuration);
                                this.resetFailedLoadAttempts();
                            }
                            catch (final Throwable throwable)
                            {
                                this.incrementFailedLoadAttempts();

                                // - only fail if the failed load attempts is greater than the maximum
                                if (this.failedLoadAttempts > serverConfiguration.getMaximumFailedLoadAttempts())
                                {
                                    throw throwable;
                                }
                            }
                        }
                    }
                    this.shutdown();
                }
                catch (final Throwable throwable)
                {
                    throw new ServerException(throwable);
                }
            }
        }
    }

    /**
     * Stores the failed load attempts.
     */
    private int failedLoadAttempts;

    /**
     * Resets the failed load attempt counter.
     */
    private void resetFailedLoadAttempts()
    {
        this.failedLoadAttempts = 0;
    }

    /**
     * Increments the failed load attempt counter.
     */
    private void incrementFailedLoadAttempts()
    {
        this.failedLoadAttempts++;
    }

    /**
     * Shuts the server down.
     */
    public void shutdown()
    {
        try
        {
            this.listener.close();
            this.listener = null;
            this.engine.shutdown();
        }
        catch (final IOException exception)
        {
            // ignore exception
        }
    }
}