001package org.andromda.core.server;
002
003import java.io.DataInputStream;
004import java.io.IOException;
005import java.io.ObjectInputStream;
006import java.io.ObjectOutputStream;
007import java.net.ServerSocket;
008import java.net.Socket;
009import java.net.SocketTimeoutException;
010import org.andromda.core.common.AndroMDALogger;
011import org.andromda.core.configuration.Configuration;
012import org.andromda.core.engine.Engine;
013
014/**
015 * The default AndroMDA {@link Server instance}.
016 *
017 * @author Chad Brandon
018 */
019public class DefaultServer
020    implements Server
021{
022    /**
023     * The message sent to the client when AndroMDA processing has completed.
024     */
025    private static final String COMPLETE = "complete";
026
027    /**
028     * The command sent to the server that indicates it
029     * should stop.
030     */
031    static final String STOP = "stop";
032
033    /**
034     * The server listener.
035     */
036    private ServerSocket listener = null;
037
038    /**
039     * The AndroMDA Engine instance.
040     */
041    private Engine engine = Engine.newInstance();
042
043    /**
044     * @see org.andromda.core.server.Server#start(org.andromda.core.configuration.Configuration)
045     */
046    public void start(final Configuration configuration)
047    {
048        engine.initialize(configuration);
049        if (configuration != null)
050        {
051            final org.andromda.core.configuration.Server serverConfiguration = configuration.getServer();
052            if (serverConfiguration != null)
053            {
054                try
055                {
056                    try
057                    {
058                        this.listener = new ServerSocket(serverConfiguration.getPort());
059                        final int modelLoadInterval = serverConfiguration.getLoadInterval();
060                        if (modelLoadInterval > 0)
061                        {
062                            this.listener.setSoTimeout(serverConfiguration.getLoadInterval());
063                        }
064                    }
065                    catch (final IOException exception)
066                    {
067                        throw new ServerException(
068                            "Could not listen on port '" + serverConfiguration.getPort() +
069                            "', change the port in your configuration");
070                    }
071                    while (true)
072                    {
073                        try
074                        {
075                            final Socket client = this.listener.accept();
076                            if (client != null)
077                            {
078                                final ObjectOutputStream serverOutput =
079                                    new ObjectOutputStream(client.getOutputStream());
080                                final ObjectInputStream objectInput =
081                                    new ObjectInputStream(new DataInputStream(client.getInputStream()));
082                                try
083                                {
084                                    final Object object = objectInput.readObject();
085                                    if (object instanceof Configuration)
086                                    {
087                                        this.engine.run((Configuration)object, false, null);
088                                    }
089                                    else if (object instanceof String)
090                                    {
091                                        final String string = (String)object;
092                                        if (string.equals(STOP))
093                                        {
094                                            break;
095                                        }
096                                    }
097                                }
098                                catch (final Throwable throwable)
099                                {
100                                    AndroMDALogger.error(throwable);
101
102                                    // - pass the exception to the client
103                                    serverOutput.writeObject(throwable);
104                                }
105
106                                // - signal to the client, it can stop waiting
107                                serverOutput.writeObject(COMPLETE);
108                                serverOutput.flush();
109                                serverOutput.close();
110                                objectInput.close();
111                                client.close();
112                            }
113                        }
114                        catch (final SocketTimeoutException exception)
115                        {
116                            try
117                            {
118                                this.engine.loadModelsIfNecessary(configuration);
119                                this.resetFailedLoadAttempts();
120                            }
121                            catch (final Throwable throwable)
122                            {
123                                this.incrementFailedLoadAttempts();
124
125                                // - only fail if the failed load attempts is greater than the maximum
126                                if (this.failedLoadAttempts > serverConfiguration.getMaximumFailedLoadAttempts())
127                                {
128                                    throw throwable;
129                                }
130                            }
131                        }
132                    }
133                    this.shutdown();
134                }
135                catch (final Throwable throwable)
136                {
137                    throw new ServerException(throwable);
138                }
139            }
140        }
141    }
142
143    /**
144     * Stores the failed load attempts.
145     */
146    private int failedLoadAttempts;
147
148    /**
149     * Resets the failed load attempt counter.
150     */
151    private void resetFailedLoadAttempts()
152    {
153        this.failedLoadAttempts = 0;
154    }
155
156    /**
157     * Increments the failed load attempt counter.
158     */
159    private void incrementFailedLoadAttempts()
160    {
161        this.failedLoadAttempts++;
162    }
163
164    /**
165     * Shuts the server down.
166     */
167    public void shutdown()
168    {
169        try
170        {
171            this.listener.close();
172            this.listener = null;
173            this.engine.shutdown();
174        }
175        catch (final IOException exception)
176        {
177            // ignore exception
178        }
179    }
180}