001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.io.remotecontrol;
003
004import static org.openstreetmap.josm.tools.I18n.marktr;
005
006import java.io.IOException;
007import java.net.BindException;
008import java.net.ServerSocket;
009import java.net.Socket;
010import java.net.SocketException;
011
012import org.openstreetmap.josm.Main;
013
014/**
015 * Simple HTTP server that spawns a {@link RequestProcessor} for every
016 * connection.
017 *
018 * Taken from YWMS plugin by frsantos.
019 */
020public class RemoteControlHttpServer extends Thread {
021
022    /** The server socket */
023    private ServerSocket server;
024
025    private static RemoteControlHttpServer instance;
026
027    /**
028     * Starts or restarts the HTTP server
029     */
030    public static void restartRemoteControlHttpServer() {
031        int port = Main.pref.getInteger("remote.control.port", 8111);
032        try {
033            stopRemoteControlHttpServer();
034
035            instance = new RemoteControlHttpServer(port);
036            instance.start();
037        } catch (BindException ex) {
038            Main.warn(marktr("Cannot start remotecontrol server on port {0}: {1}"),
039                    Integer.toString(port), ex.getLocalizedMessage());
040        } catch (IOException ioe) {
041            Main.error(ioe);
042        }
043    }
044
045    /**
046     * Stops the HTTP server
047     * @since 5861
048     */
049    public static void stopRemoteControlHttpServer() {
050        if (instance != null) {
051            try {
052                instance.stopServer();
053                instance = null;
054            } catch (IOException ioe) {
055                Main.error(ioe);
056            }
057        }
058    }
059
060    /**
061     * Constructor
062     * @param port The port this server will listen on
063     * @throws IOException when connection errors
064     */
065    public RemoteControlHttpServer(int port) throws IOException {
066        super("RemoteControl HTTP Server");
067        this.setDaemon(true);
068        // Start the server socket with only 1 connection.
069        // Also make sure we only listen on the local interface so nobody from the outside can connect!
070        // NOTE: On a dual stack machine with old Windows OS this may not listen on both interfaces!
071        this.server = new ServerSocket(port, 1, RemoteControl.getInetAddress());
072    }
073
074    /**
075     * The main loop, spawns a {@link RequestProcessor} for each connection
076     */
077    @Override
078    public void run() {
079        Main.info(marktr("RemoteControl::Accepting connections on {0}:{1}"),
080                server.getInetAddress(), Integer.toString(server.getLocalPort()));
081        while (true) {
082            try {
083                @SuppressWarnings("resource")
084                Socket request = server.accept();
085                RequestProcessor.processRequest(request);
086            } catch (SocketException se) {
087                if (!server.isClosed())
088                    Main.error(se);
089            } catch (IOException ioe) {
090                Main.error(ioe);
091            }
092        }
093    }
094
095    /**
096     * Stops the HTTP server
097     *
098     * @throws IOException
099     */
100    public void stopServer() throws IOException {
101        server.close();
102        Main.info(marktr("RemoteControl::Server stopped."));
103    }
104}