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.ServerSocket;
008import java.net.Socket;
009import java.net.SocketException;
010
011import org.openstreetmap.josm.Main;
012
013/**
014 * Simple HTTP server that spawns a {@link RequestProcessor} for every
015 * connection.
016 *
017 * Taken from YWMS plugin by frsantos.
018 */
019public class RemoteControlHttpServer extends Thread {
020
021    /** The server socket */
022    private final ServerSocket server;
023
024    /** The server instance for IPv4 */
025    private static volatile RemoteControlHttpServer instance4;
026    /** The server instance for IPv6 */
027    private static volatile RemoteControlHttpServer instance6;
028
029    /**
030     * Starts or restarts the HTTP server
031     */
032    public static void restartRemoteControlHttpServer() {
033        stopRemoteControlHttpServer();
034        int port = Main.pref.getInteger("remote.control.port", 8111);
035        try {
036            instance4 = new RemoteControlHttpServer(port, false);
037            instance4.start();
038        } catch (Exception ex) {
039            Main.warn(marktr("Cannot start IPv4 remotecontrol server on port {0}: {1}"),
040                    Integer.toString(port), ex.getLocalizedMessage());
041        }
042        try {
043            instance6 = new RemoteControlHttpServer(port, true);
044            instance6.start();
045        } catch (Exception ex) {
046            /* only show error when we also have no IPv4 */
047            if (instance4 == null) {
048                Main.warn(marktr("Cannot start IPv6 remotecontrol server on port {0}: {1}"),
049                    Integer.toString(port), ex.getLocalizedMessage());
050            }
051        }
052    }
053
054    /**
055     * Stops the HTTP server
056     * @since 5861
057     */
058    public static void stopRemoteControlHttpServer() {
059        if (instance4 != null) {
060            try {
061                instance4.stopServer();
062            } catch (IOException ioe) {
063                Main.error(ioe);
064            }
065            instance4 = null;
066        }
067        if (instance6 != null) {
068            try {
069                instance6.stopServer();
070            } catch (IOException ioe) {
071                Main.error(ioe);
072            }
073            instance6 = null;
074        }
075    }
076
077    /**
078     * Constructor
079     * @param port The port this server will listen on
080     * @param ipv6 Whether IPv6 or IPv4 server should be started
081     * @throws IOException when connection errors
082     * @since 8339
083     */
084    public RemoteControlHttpServer(int port, boolean ipv6) throws IOException {
085        super("RemoteControl HTTP Server");
086        this.setDaemon(true);
087        this.server = new ServerSocket(port, 1, ipv6 ?
088            RemoteControl.getInet6Address() : RemoteControl.getInet4Address());
089    }
090
091    /**
092     * The main loop, spawns a {@link RequestProcessor} for each connection
093     */
094    @Override
095    public void run() {
096        Main.info(marktr("RemoteControl::Accepting remote connections on {0}:{1}"),
097                server.getInetAddress(), Integer.toString(server.getLocalPort()));
098        while (true) {
099            try {
100                @SuppressWarnings("resource")
101                Socket request = server.accept();
102                RequestProcessor.processRequest(request);
103            } catch (SocketException se) {
104                if (!server.isClosed()) {
105                    Main.error(se);
106                } else {
107                    // stop the thread automatically if server is stopped
108                    return;
109                }
110            } catch (IOException ioe) {
111                Main.error(ioe);
112            }
113        }
114    }
115
116    /**
117     * Stops the HTTP server
118     *
119     * @throws IOException if any I/O error occurs
120     */
121    public void stopServer() throws IOException {
122        Main.info(marktr("RemoteControl::Server {0}:{1} stopped."),
123        server.getInetAddress(), Integer.toString(server.getLocalPort()));
124        server.close();
125    }
126}