at.uni_salzburg.cs.ckgroup.apos.AposNtripCasterMock.java Source code

Java tutorial

Introduction

Here is the source code for at.uni_salzburg.cs.ckgroup.apos.AposNtripCasterMock.java

Source

/*
 * @(#) AposNtripCasterMock.java
 *
 * This code is part of the JAviator project: javiator.cs.uni-salzburg.at
 * Copyright (c) 2009  Clemens Krainer
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */
package at.uni_salzburg.cs.ckgroup.apos;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.net.URL;
import java.util.Properties;

import org.apache.commons.codec.binary.Base64;

public class AposNtripCasterMock {
    public static final String PROP_INPUT_DATA_FILE = "input.data.file";
    public static final String PROP_EXPECTED_LOCATION = "expected.location";

    /**
     * This flag indicates the state of the mail server
     */
    private boolean ntripCasterRunning;

    /**
     * The port number for the NTRIP caster to listen.
     */
    private int port;

    /**
     * The basic authentication string for the APOS NTRIP service. 
     */
    private byte[] basicAuthentication;

    /**
     * This variable contains the reference to the NTRIP caster service.
     */
    private NtripCasterThread ntripCaster;

    /**
     * 
     */
    private String expectedRequest;
    private String expectedUserAgent;
    private String expectedAuthorisation;
    private String expectedLocation;

    private File inputDataFile;

    /**
     * Construct a NTRIP mock caster.
     * 
     * @param props the properties for the NTRIP mock caster.
     * @throws FileNotFoundException 
     */
    public AposNtripCasterMock(Properties props) throws FileNotFoundException {
        init(props);
    }

    /**
     * Initialize the NTRIP mock caster.
     * @throws FileNotFoundException 
     */
    private void init(Properties props) throws FileNotFoundException {

        port = Integer.parseInt(props.getProperty(AposNtrip.PROP_PORT));
        String userName = props.getProperty(AposNtrip.PROP_USERNAME);
        String password = props.getProperty(AposNtrip.PROP_PASSWORD);
        String mountPoint = props.getProperty(AposNtrip.PROP_MOUNTPOINT);

        byte[] encodedPassword = (userName + ":" + password).getBytes();
        Base64 encoder = new Base64();
        basicAuthentication = encoder.encode(encodedPassword);

        expectedRequest = "GET /" + mountPoint + " HTTP/1.0";
        expectedUserAgent = "User-Agent: .*";
        expectedAuthorisation = "Authorization: Basic " + (new String(basicAuthentication));
        expectedLocation = props.getProperty(PROP_EXPECTED_LOCATION);

        System.out.println("AposNtripCasterMock: expectedRequest=" + expectedRequest);
        System.out.println("AposNtripCasterMock: expectedUserAgent=" + expectedUserAgent);
        System.out.println("AposNtripCasterMock: expectedAuthorisation=" + expectedAuthorisation);

        String fileName = props.getProperty(PROP_INPUT_DATA_FILE);
        URL url = Thread.currentThread().getContextClassLoader().getResource(fileName);

        if (url == null)
            throw new FileNotFoundException(fileName);

        inputDataFile = new File(url.getFile());
    }

    /**
     * This method creates the NTRIP caster thread and starts it.
     *
     * @throws IllegalStateException thrown if the mail server already runs
     */
    public void startCasterThread() throws IllegalStateException {
        System.out.println("Start Mailserver on port " + port);
        if (ntripCaster != null)
            throw new IllegalStateException("NTRIP caster is already running.");

        ntripCasterRunning = false;

        ntripCaster = new NtripCasterThread();
        ntripCaster.start();

        // Now we wait for the NTRIP caster to open its socket.
        int cycles = 100;
        while (!ntripCasterRunning && --cycles > 0) {
            try {
                Thread.sleep(50);
            } catch (Exception e) {
            }
            Thread.yield();
        }

        if (cycles == 0)
            throw new IllegalStateException("Can not launch NTRIP caster.");
    }

    /**
     * This method stops the running NTRIP caster thread
     *
     * @throws IllegalStateException thrown if the mail server does not run
     */
    public void stopCasterThread() throws IllegalStateException {
        System.out.println("\nStop NTRIP Caster on port " + port);
        if (ntripCaster == null)
            throw new IllegalStateException("NTRIP caster thread does not run.");

        ntripCaster.terminate();

        // Now we wait for the NTRIP caster to diminish.
        int cycles = 100;
        while (ntripCasterRunning && --cycles > 0) {
            try {
                Thread.sleep(50);
            } catch (Exception e) {
            }
            Thread.yield();
        }

        ntripCaster = null;
    }

    /**
    * This class implements the NTRIP caster thread. It opens a
    * <code>ServerSocket</code> at the specified port number and waits for
    * clients to connect. It is a <code>NtripCasterWorkerThread</code> that
    * takes an incoming connection and does the actual receiving and sending.
    */
    private class NtripCasterThread extends Thread {
        ServerSocket server_socket;

        /**
        * If <code>active</code> equals to <code>true</code> the NTRIP
        * caster thread waits for incoming connections.
        */
        private boolean active = true;

        /* (non-Javadoc)
         * @see java.lang.Thread#run()
         */
        public void run() {
            System.out.println("NtripCasterThread: start. port=" + port);

            try {
                server_socket = new ServerSocket(port);
                System.out.println("NtripCasterThread: Server started");

                ntripCasterRunning = true;

                while (active) {
                    Socket client_socket = server_socket.accept();

                    if (!active)
                        break;

                    NtripCasterWorkerThread worker = new NtripCasterWorkerThread(client_socket);

                    worker.start();

                    System.out.println("NtripCasterThread: New client connected");
                }
            } catch (IOException e) {
                if (!(e instanceof SocketException) && !e.getMessage().equals("Socket closed"))
                    e.printStackTrace();
                terminate();
                return;
            }

            System.out.println("NtripCasterThread: end.(" + (active ? "voluntarily" : "forced") + ")");
        }

        /**
         * Terminate the mail server thread.
         */
        public void terminate() {
            active = false;
            try {
                server_socket.close();
            } catch (IOException e) {
            }
            yield();
        }
    }

    /**
     * This class implements a mail server worker thread that inherits an
     * established connection and receives the email from the client. It is this
     * class that implements the EHLO protocol.
     */
    private class NtripCasterWorkerThread extends Thread {
        /**
         * The inherited socket with an established connection.
         */
        private Socket socket;

        /**
         * The automaton state constants. 
         */
        private static final int STATE_CONNECTION_OPEN = 0;
        private static final int STATE_REQUEST_RECEIVED = 1;
        private static final int STATE_AGENT_RECEIVED = 2;
        private static final int STATE_AUTORISATION_RECEIVED = 3;
        private static final int STATE_HEADER_COMPLETE = 4;
        private static final int STATE_LOCATION_RECEIVED = 5;
        private static final int STATE_SENDING_DATA = 6;
        private static final int STATE_INVALID_HEADER = 7;

        /**
         * The current state of the automaton. See above at "Automaton
         * Constants".
         */
        private int state;

        /**
         * Set to <code>true</code> if a NTRIP caster worker thread
         * communicates to a client.
         */
        private boolean running;

        /**
         * Constructor.
         *
         * @param socket a socket containing an already established connection
         *        to a client.
         */
        public NtripCasterWorkerThread(Socket socket) {
            this.socket = socket;
            state = 0;
        }

        /* (non-Javadoc)
         * @see java.lang.Thread#run()
         */
        public void run() {
            running = true;

            try {
                InputStream socket_in = socket.getInputStream();
                InputStreamReader isr = new InputStreamReader(socket_in, "8859_1");
                BufferedReader client_in = new BufferedReader(isr);
                OutputStream client_out = socket.getOutputStream();

                while (running) {

                    while (state != STATE_LOCATION_RECEIVED && running) {

                        String client_str = client_in.readLine();

                        System.out.println("Client message: '" + client_str + "'");

                        state = handleMessage(client_str);
                        System.out.println("New State: '" + state + "'");

                        if (state == STATE_INVALID_HEADER) {
                            running = false;
                            socket.close();
                        }
                    }

                    if (!running)
                        break;

                    state = STATE_SENDING_DATA;

                    FileInputStream data = new FileInputStream(inputDataFile);

                    int ch = 0;
                    int charCounter = 0;
                    while (state == STATE_SENDING_DATA && running && ch >= 0) {
                        ch = data.read();
                        if (ch >= 0) {
                            client_out.write(ch);
                            ++charCounter;
                        }
                    }

                    client_out.flush();
                    client_out.close();

                    System.out.println("\nNtripCasterWorkerThread: characters written: " + charCounter);
                }

            } catch (IOException e) {
                if (!(e instanceof SocketException) || !e.getMessage().equals("Socket closed")) {
                    System.out.println("NtripCasterWorkerThread: I/O error 1 " + e);
                }

            }

            try {
                socket.close();
            } catch (IOException e) {
                System.out.println("NtripCasterWorkerThread: I/O error 2 " + e);
            }
        }

        /**
         * Handle a client message.
         *
         * @param msg the client message.
         * @return the new state.
         */
        private int handleMessage(String msg) {
            if (state == STATE_CONNECTION_OPEN && msg.equals(expectedRequest))
                return STATE_REQUEST_RECEIVED;

            if (state == STATE_REQUEST_RECEIVED && msg.matches(expectedUserAgent))
                return STATE_AGENT_RECEIVED;

            if (state == STATE_AGENT_RECEIVED && msg.equals(expectedAuthorisation))
                return STATE_AUTORISATION_RECEIVED;

            if (state == STATE_AUTORISATION_RECEIVED && msg.equals(""))
                return STATE_HEADER_COMPLETE;

            if (state == STATE_HEADER_COMPLETE && msg.equals(expectedLocation))
                return STATE_LOCATION_RECEIVED;

            return STATE_INVALID_HEADER;
        }

    }

}