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 (IOException ex) {
039            Main.debug(ex);
040            Main.warn(marktr("Cannot start IPv4 remotecontrol server on port {0}: {1}"),
041                    Integer.toString(port), ex.getLocalizedMessage());
042        }
043        try {
044            instance6 = new RemoteControlHttpServer(port, true);
045            instance6.start();
046        } catch (IOException ex) {
047            /* only show error when we also have no IPv4 */
048            if (instance4 == null) {
049                Main.debug(ex);
050                Main.warn(marktr("Cannot start IPv6 remotecontrol server on port {0}: {1}"),
051                    Integer.toString(port), ex.getLocalizedMessage());
052            }
053        }
054    }
055
056    /**
057     * Stops the HTTP server
058     * @since 5861
059     */
060    public static void stopRemoteControlHttpServer() {
061        if (instance4 != null) {
062            try {
063                instance4.stopServer();
064            } catch (IOException ioe) {
065                Main.error(ioe);
066            }
067            instance4 = null;
068        }
069        if (instance6 != null) {
070            try {
071                instance6.stopServer();
072            } catch (IOException ioe) {
073                Main.error(ioe);
074            }
075            instance6 = null;
076        }
077    }
078
079    /**
080     * Constructor
081     * @param port The port this server will listen on
082     * @param ipv6 Whether IPv6 or IPv4 server should be started
083     * @throws IOException when connection errors
084     * @since 8339
085     */
086    public RemoteControlHttpServer(int port, boolean ipv6) throws IOException {
087        super("RemoteControl HTTP Server");
088        this.setDaemon(true);
089        this.server = new ServerSocket(port, 1, ipv6 ?
090            RemoteControl.getInet6Address() : RemoteControl.getInet4Address());
091    }
092
093    /**
094     * The main loop, spawns a {@link RequestProcessor} for each connection
095     */
096    @Override
097    public void run() {
098        Main.info(marktr("RemoteControl::Accepting remote connections on {0}:{1}"),
099                server.getInetAddress(), Integer.toString(server.getLocalPort()));
100        while (true) {
101            try {
102                @SuppressWarnings("resource")
103                Socket request = server.accept();
104                RequestProcessor.processRequest(request);
105            } catch (SocketException se) {
106                if (!server.isClosed()) {
107                    Main.error(se);
108                } else {
109                    // stop the thread automatically if server is stopped
110                    return;
111                }
112            } catch (IOException ioe) {
113                Main.error(ioe);
114            }
115        }
116    }
117
118    /**
119     * Stops the HTTP server
120     *
121     * @throws IOException if any I/O error occurs
122     */
123    public void stopServer() throws IOException {
124        Main.info(marktr("RemoteControl::Server {0}:{1} stopped."),
125        server.getInetAddress(), Integer.toString(server.getLocalPort()));
126        server.close();
127    }
128}