com.codeveo.lago.bot.stomp.client.AbstractHttpLagoBot.java Source code

Java tutorial

Introduction

Here is the source code for com.codeveo.lago.bot.stomp.client.AbstractHttpLagoBot.java

Source

/*
 * Copyright 2014 Ladislav Klenovic <klenovic@nerdrobot.net>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.codeveo.lago.bot.stomp.client;

import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

import javax.websocket.ContainerProvider;
import javax.websocket.WebSocketContainer;

import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.messaging.Message;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.socket.WebSocketHttpHeaders;
import org.springframework.web.socket.client.standard.StandardWebSocketClient;
import org.springframework.web.socket.sockjs.client.RestTemplateXhrTransport;
import org.springframework.web.socket.sockjs.client.SockJsClient;
import org.springframework.web.socket.sockjs.client.Transport;
import org.springframework.web.socket.sockjs.client.WebSocketTransport;

import com.codeveo.lago.common.shared.LagoRequestHelper;
import com.codeveo.lago.common.shared.RFault;
import com.codeveo.lago.common.shared.WebServiceUserDesc;
import com.codeveo.lago.communication.rest.api.LagoMessageHandler;
import com.codeveo.lago.communication.rest.api.RClientMessage;
import com.codeveo.lago.security.management.rest.api.RLagoBot;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.module.jaxb.JaxbAnnotationModule;
import com.ning.http.client.AsyncHttpClient;
import com.ning.http.client.AsyncHttpClientConfig;
import com.ning.http.client.AsyncHttpClientConfig.Builder;
import com.ning.http.client.Realm;
import com.ning.http.client.Realm.AuthScheme;

public abstract class AbstractHttpLagoBot implements LagoMessageHandler {
    private static final Logger LOG = LoggerFactory.getLogger(AbstractHttpLagoBot.class);
    private static final int THREAD_POOL_SIZE = 25;
    private static final long CLIENT_MAX_IDLE_TIMEOUT = 30 * 60 * 1000;
    private final LagoJsonMessageConverter converter = new LagoJsonMessageConverter();
    private final BlockingQueue<Message<byte[]>> queue = new LinkedBlockingQueue<>();
    private final ObjectMapper objectMapper;
    private AsyncHttpClient asyncHttpClient;
    private WebSocketStompClient stompClient;
    private WebSocketHttpHeaders wsHeaders = new WebSocketHttpHeaders();;
    private String cookie;
    private ThreadPoolTaskExecutor taskExecutor;
    private StandardWebSocketClient webSocketClient;
    private SockJsClient sockJsClient;

    protected RLagoBot lagoBot;
    protected HttpLagoRequestHelper httpLagoRequestHelper;

    protected AbstractHttpLagoBot() {
        objectMapper = new ObjectMapper();
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

        /* jaxb only */
        JaxbAnnotationModule module = new JaxbAnnotationModule();
        objectMapper.registerModule(module);

        taskExecutor = new ThreadPoolTaskExecutor();
        taskExecutor.setMaxPoolSize(THREAD_POOL_SIZE);
        taskExecutor.initialize();

        WebSocketContainer webSocketContainer = ContainerProvider.getWebSocketContainer();
        webSocketContainer.setDefaultMaxSessionIdleTimeout(CLIENT_MAX_IDLE_TIMEOUT);

        webSocketClient = new StandardWebSocketClient(webSocketContainer);
        webSocketClient.setTaskExecutor(taskExecutor);
    }

    protected void setupAndSignInClient(String lagoUrl, String botUsername, String botPassword,
            LagoRequestHelper requestHelper) throws Exception {
        try {
            if (requestHelper == null) {
                throw new Exception("Request helper must not be null");
            }

            if (!(requestHelper instanceof HttpLagoRequestHelper)) {
                throw new Exception("Requires instance of '" + HttpLagoRequestHelper.class.getSimpleName()
                        + "' not '" + requestHelper.getClass().getSimpleName() + "'");
            }

            HttpLagoRequestHelper httpRequestHelper = (HttpLagoRequestHelper) requestHelper;

            lagoBot = new RLagoBot(null, null, botUsername, botPassword);

            Builder builder = new AsyncHttpClientConfig.Builder();
            Realm realm = new Realm.RealmBuilder().setPrincipal(botUsername).setPassword(botPassword)
                    .setUsePreemptiveAuth(true).setScheme(AuthScheme.BASIC).build();
            builder.setRealm(realm).build();

            asyncHttpClient = new AsyncHttpClient(builder.build());

            Pair<Integer, String> statusWithCookieOrFaultJSON = HttpLagoRequestHelper.signIn(lagoUrl, botUsername,
                    botPassword);

            int statusCode = statusWithCookieOrFaultJSON.getLeft();

            if (statusCode == HttpStatus.CREATED.value()) {
                cookie = statusWithCookieOrFaultJSON.getRight();

                /* websocket headers */
                wsHeaders.add("Cookie", cookie);
            } else {
                String faultJSON = statusWithCookieOrFaultJSON.getRight();

                if (faultJSON != null) {
                    RFault fault = objectMapper.readValue(faultJSON, RFault.class);

                    if (fault != null) {
                        throw new Exception("Error occured during authentication (HTTP status code: " + statusCode
                                + ", Error code: " + fault.getErrorCode() + ", Message: '" + fault.getMessage()
                                + "')");
                    }
                }

                throw new Exception("Error occured during authentication (HTTP status code: " + statusCode);
            }

            if (cookie == null) {
                throw new Exception("Bot '" + botUsername + "' could not sign in");
            }

            /* setup request helper */
            httpRequestHelper.setAsyncHttpClient(asyncHttpClient).setCookie(cookie).setObjectMapper(objectMapper)
                    .setServerUrl(lagoUrl);
            this.httpLagoRequestHelper = httpRequestHelper;

            String wsLagoUrl = toWsUrl(lagoUrl);
            URI uri = new URI(wsLagoUrl + WebServiceUserDesc.WS_PATH);

            LagoStompMessageHandler messageListener = new LagoStompMessageHandler(lagoBot, queue);

            List<Transport> transports = new ArrayList<>();
            transports.add(new WebSocketTransport(new StandardWebSocketClient()));
            RestTemplateXhrTransport xhrTransport = new RestTemplateXhrTransport(new RestTemplate());
            xhrTransport.setRequestHeaders(wsHeaders);
            transports.add(xhrTransport);

            sockJsClient = new SockJsClient(transports);
            stompClient = new WebSocketStompClient(uri, wsHeaders, sockJsClient);
            stompClient.setMessageConverter(converter);
            stompClient.connect(messageListener);

            taskExecutor.submit(new StompMessageListener());
        } catch (Exception e) {
            if (cookie != null) {
                httpLagoRequestHelper.signOut();
            }

            throw new Exception("Error occured while initializing lago bot", e);
        }
    }

    private String toWsUrl(String lagoUrl) {
        return lagoUrl.replaceFirst("http://", "ws://");
    }

    private class StompMessageListener implements Runnable {
        @Override
        public void run() {
            try {
                Thread.currentThread().setName("lagoBotClientMessageHandler");
                Message<byte[]> message;

                while ((message = queue.take()) != null) {
                    RClientMessage rClientMessage = (RClientMessage) converter.fromMessage(message,
                            RClientMessage.class);

                    handleMessage(rClientMessage);
                }
            } catch (Throwable e) {
                LOG.error("Error has occured in runtime of worker thread", e);
            } finally {
                httpLagoRequestHelper.signOut();
            }
        }
    }
}