org.deviceconnect.message.http.event.HttpEventManager.java Source code

Java tutorial

Introduction

Here is the source code for org.deviceconnect.message.http.event.HttpEventManager.java

Source

/*
 HttpEventManager.java
 Copyright (c) 2014 NTT DOCOMO,INC.
 Released under the MIT license
 http://opensource.org/licenses/mit-license.php
 */
package org.deviceconnect.message.http.event;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.logging.Logger;

import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.DefaultHttpClient;
import org.deviceconnect.message.DConnectMessage;
import org.deviceconnect.message.event.AbstractEventManager;
import org.deviceconnect.utils.URIBuilder;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;
import org.json.JSONException;
import org.json.JSONObject;

/**
 * ?. 
 * ???WebSocket?????
 * 
 * 
 * @author NTT DOCOMO, INC.
 */
public final class HttpEventManager extends AbstractEventManager {

    /** 
     * WebSocket.
     */
    private static final int NORMAL_CLOSE_CODE = 1000;

    /** 
     * ?.
     */
    private static final int RETRY_TIMES = 5;

    /** 
     * ??.
     * ??????????????
     * ex. 1 {@value} ?2 {@value} * 2
     */
    private static final long RETRY_WAIT = 30 * 1000;

    /**
     * ?EventManager?.
     */
    public static final HttpEventManager INSTANCE = new HttpEventManager();

    /**
     * WebSocket.
     */
    private EventWebSocketClient mWSClient;

    /** 
     * ??.
     */
    private CloseHandler mCloseHandler;

    /**
     * .
     */
    private Logger mLogger = Logger.getLogger("org.deviceconnect.sdk");

    /**
     * .
     */
    private Object mLock;

    /**
     * WebSocket.
     * 
     * @author NTT DOCOMO, INC.
     * 
     */
    private enum Status {

        /** 
         * ?.
         */
        WAITING_OPEN,

        /**
         * ???.
         */
        OPEN,

        /**
         * ???.
         */
        CLOSE,

        /**
         * .
         */
        RETRYING,
    }

    /**
     * WebSocket.
     */
    private Status mStatus;

    /**
     * ???private.
     */
    private HttpEventManager() {
        mLock = new Object();
        mStatus = Status.CLOSE;
    }

    /**
     * ????. <br/>
     * ????????????????<br/>
     * ???????????{@link #disconnect()}???????
     * ???????????????????????
     * 
     * @param host ??
     * @param port ??
     * @param isSSL ??SSL???????
     * @param sessionKey 
     * @param handler ??????????
     * @return ?????true?????false?
     */
    public synchronized boolean connect(final String host, final int port, final boolean isSSL,
            final String sessionKey, final CloseHandler handler) {

        if (host == null) {
            throw new IllegalArgumentException("host must not be null.");
        } else if (port < 0) {
            throw new IllegalArgumentException("port must be larger than 0.");
        } else if (sessionKey == null) {
            throw new IllegalArgumentException("sessionKey must not be null.");
        }

        if (mStatus == Status.OPEN || mStatus == Status.RETRYING) {

            if (!sessionKey.equals(getSessionKey())) {
                setSessionKey(sessionKey);
                if (mStatus == Status.OPEN) {
                    mWSClient.sendSessionKey(sessionKey);
                }
            }

            return true;
        }

        URIBuilder builder = new URIBuilder();
        if (isSSL) {
            builder.setScheme("wss");
        } else {
            builder.setScheme("ws");
        }

        builder.setHost(host).setPort(port).setPath("/" + DConnectMessage.EXTRA_WEBSOCKET);

        URI webSocketUri;
        try {
            webSocketUri = builder.build();
        } catch (URISyntaxException e) {
            throw new IllegalArgumentException("Can not create uri. Do check the parameters.");
        }

        setSessionKey(sessionKey);
        mStatus = Status.WAITING_OPEN;
        mWSClient = new EventWebSocketClient(webSocketUri);
        mWSClient.connect();

        waitConnect();

        if (mWSClient.isOpen()) {
            mCloseHandler = handler;
            return true;
        }

        setSessionKey(null);
        return false;
    }

    @Override
    public synchronized void disconnect() {
        if (mWSClient != null && mWSClient.isOpen()) {
            mWSClient.close();
            mWSClient = null;
            mStatus = Status.CLOSE;
        }
    }

    /**
     * ??.
     */
    private synchronized void retry() {
        mStatus = Status.RETRYING;
        new Thread(new RetryProcess()).start();
    }

    /**
     * WebSocket??.
     */
    private void waitConnect() {
        synchronized (mLock) {
            try {
                mLock.wait();
            } catch (InterruptedException e) {
                mWSClient.close();
            }
        }
    }

    @Override
    protected HttpResponse execute(final HttpUriRequest request) throws IOException {
        DefaultHttpClient client = new DefaultHttpClient();
        HttpResponse response = client.execute(request);
        HttpResponse retRes = copyResponse(response);
        client.getConnectionManager().shutdown();
        return retRes;
    }

    /**
     * Event??WebSocket?.
     * 
     * @author NTT DOCOMO, INC.
     * 
     */
    private class EventWebSocketClient extends WebSocketClient {

        /**
         * WebSocket??.
         * 
         * @param serverURI ??URI
         */
        public EventWebSocketClient(final URI serverURI) {
            super(serverURI);
        }

        @Override
        public void onOpen(final ServerHandshake handshakedata) {
            mStatus = Status.OPEN;
            sendSessionKey(getSessionKey());
            synchronized (mLock) {
                mLock.notifyAll();
            }
        }

        @Override
        public void onMessage(final String message) {

            try {
                sendEvent(new JSONObject(message));
            } catch (JSONException e) {
                mLogger.warning("EventWebSocketClient#onMessage Invalid message. : " + e.getMessage());
            }

        }

        @Override
        public void onClose(final int code, final String reason, final boolean remote) {

            synchronized (HttpEventManager.this) {
                if (mStatus == Status.WAITING_OPEN || mStatus == Status.RETRYING) {
                    synchronized (mLock) {
                        mLock.notifyAll();
                    }
                } else if (mStatus == Status.OPEN) {
                    if (code != NORMAL_CLOSE_CODE) {
                        // ??????
                        retry();
                        return;
                    } else if (mCloseHandler != null) {
                        mCloseHandler.onClosed();
                    }
                }

                if (mStatus != Status.RETRYING) {
                    mStatus = Status.CLOSE;
                }
            }
        }

        @Override
        public synchronized void onError(final Exception ex) {
            mLogger.warning("EventWebSocketClient#onError : " + ex);
        }

        /**
         * ??.
         * 
         * @param sessionKey 
         */
        public void sendSessionKey(final String sessionKey) {
            send("{\"" + DConnectMessage.EXTRA_SESSION_KEY + "\":\"" + sessionKey + "\"}");
        }
    }

    /**
     * ??.
     * 
     * @author NTT DOCOMO, INC.
     *
     */
    private class RetryProcess implements Runnable {

        @Override
        public void run() {
            mLogger.fine("RetryProcess#run. Retrying...");
            for (int i = 0; i < RETRY_TIMES; i++) {
                long wait = RETRY_WAIT * (i + 1);
                if (retry(wait)) {
                    mLogger.fine("RetryProcess#run. Successed to retry.");
                    return;
                }
            }

            disconnect();
            if (mCloseHandler != null) {
                mLogger.fine("RetryProcess#run. Failed to retry.");
                mCloseHandler.onClosed();
            }
        }

        /**
         * ??.
         * 
         * @param wait ??????????????????????
         * @return ????true???false?
         */
        private boolean retry(final long wait) {

            try {
                Thread.sleep(wait);
            } catch (InterruptedException e) {
                mLogger.warning("RetryProcess#retry Interrupted. : " + e.getMessage());
            }

            mWSClient = new EventWebSocketClient(mWSClient.getURI());
            mWSClient.connect();
            waitConnect();
            if (mWSClient.isOpen()) {
                return true;
            }

            return false;
        }
    }
}