Java tutorial
/* * 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(); } } } }