Java tutorial
/** * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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 org.apache.camel.component.box.internal; import java.net.SocketException; import java.net.SocketTimeoutException; import java.util.ArrayList; import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; import com.box.boxjavalibv2.dao.BoxCollection; import com.box.boxjavalibv2.dao.BoxEventCollection; import com.box.boxjavalibv2.dao.BoxRealTimeServer; import com.box.boxjavalibv2.dao.BoxTypedObject; import com.box.boxjavalibv2.exceptions.AuthFatalFailureException; import com.box.boxjavalibv2.exceptions.BoxServerException; import com.box.boxjavalibv2.requests.requestobjects.BoxEventRequestObject; import com.box.boxjavalibv2.resourcemanagers.IBoxEventsManager; import com.box.restclientv2.exceptions.BoxRestException; import com.box.restclientv2.exceptions.BoxSDKException; import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.camel.RuntimeCamelException; import org.apache.camel.util.ObjectHelper; import org.apache.http.HttpEntity; import org.apache.http.HttpHeaders; import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.StatusLine; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.params.BasicHttpParams; import org.apache.http.params.HttpConnectionParams; import org.apache.http.params.HttpParams; import org.apache.http.protocol.HttpContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Manager for monitoring events using long polling. */ @SuppressWarnings("deprecation") public class LongPollingEventsManager { private static final Logger LOG = LoggerFactory.getLogger(LongPollingEventsManager.class); private static final String RETRY_TIMEOUT = "retry_timeout"; private static final String MAX_RETRIES = "max_retries"; private static final String MESSAGE = "message"; private static final String NEW_CHANGE = "new_change"; private static final String RECONNECT = "reconnect"; private static final String OUT_OF_DATE = "out_of_date"; private final CachedBoxClient cachedBoxClient; private final ExecutorService executorService; private final BasicHttpParams httpParams; private HttpClient httpClient; private Future<?> pollFuture; private HttpGet httpGet; private boolean done; public LongPollingEventsManager(CachedBoxClient boxClient, Map<String, Object> httpParams, ExecutorService executorService) { this.cachedBoxClient = boxClient; this.executorService = executorService; this.httpParams = new BasicHttpParams(); HttpConnectionParams.setSoKeepalive(this.httpParams, true); if (httpParams != null) { for (Map.Entry<String, Object> entry : httpParams.entrySet()) { this.httpParams.setParameter(entry.getKey(), entry.getValue()); } } } @SuppressWarnings("unused") public void poll(long streamPosition, final String streamType, final int limit, final EventCallback callback) throws BoxServerException, AuthFatalFailureException, BoxRestException { // get BoxClient Event Manager final IBoxEventsManager eventsManager = cachedBoxClient.getBoxClient().getEventsManager(); // get current stream position if requested if (BoxEventRequestObject.STREAM_POSITION_NOW == streamPosition) { streamPosition = getCurrentStreamPosition(eventsManager, streamPosition); } // validate parameters ObjectHelper.notNull(streamPosition, "streamPosition"); ObjectHelper.notEmpty(streamType, "streamType"); ObjectHelper.notNull(callback, "eventCallback"); httpClient = new DefaultHttpClient(cachedBoxClient.getClientConnectionManager(), httpParams); // start polling thread LOG.info("Started event polling thread for " + cachedBoxClient); final long startStreamPosition = streamPosition; pollFuture = executorService.submit(new Runnable() { @Override public void run() { final ObjectMapper mapper = new ObjectMapper(); long currentStreamPosition = startStreamPosition; BoxRealTimeServer realTimeServer = null; boolean retry = false; int retries = 0; int maxRetries = 1; while (!done) { try { // set to true if no exceptions thrown retry = false; if (realTimeServer == null) { // get RTS URL realTimeServer = getBoxRealTimeServer(currentStreamPosition, eventsManager); // update HTTP timeout final int requestTimeout = Integer .parseInt(realTimeServer.getExtraData(RETRY_TIMEOUT).toString()); final HttpParams params = httpClient.getParams(); HttpConnectionParams.setSoTimeout(params, requestTimeout * 1000); // update maxRetries maxRetries = Integer.parseInt(realTimeServer.getExtraData(MAX_RETRIES).toString()); } // create HTTP request for RTS httpGet = getPollRequest(realTimeServer.getUrl(), currentStreamPosition); // execute RTS poll HttpResponse httpResponse = null; try { httpResponse = httpClient.execute(httpGet, (HttpContext) null); } catch (SocketTimeoutException e) { LOG.debug("Poll timed out, retrying for " + cachedBoxClient); } if (httpResponse != null) { // parse response final StatusLine statusLine = httpResponse.getStatusLine(); if (statusLine != null && statusLine.getStatusCode() == HttpStatus.SC_OK) { final HttpEntity entity = httpResponse.getEntity(); @SuppressWarnings("unchecked") Map<String, String> rtsResponse = mapper.readValue(entity.getContent(), Map.class); final String message = rtsResponse.get(MESSAGE); if (NEW_CHANGE.equals(message)) { // get events final BoxEventRequestObject requestObject = BoxEventRequestObject .getEventsRequestObject(currentStreamPosition); requestObject.setStreamType(streamType); requestObject.setLimit(limit); final BoxEventCollection events = eventsManager.getEvents(requestObject); // notify callback callback.onEvent(events); // update stream position currentStreamPosition = events.getNextStreamPosition(); } else if (RECONNECT.equals(message) || MAX_RETRIES.equals(message)) { LOG.debug("Long poll reconnect for " + cachedBoxClient); realTimeServer = null; } else if (OUT_OF_DATE.equals(message)) { // update currentStreamPosition LOG.debug("Long poll out of date for " + cachedBoxClient); currentStreamPosition = getCurrentStreamPosition(eventsManager, BoxEventRequestObject.STREAM_POSITION_NOW); realTimeServer = null; } else { throw new RuntimeCamelException("Unknown poll response " + message); } } else { String msg = "Unknown error"; if (statusLine != null) { msg = String.format("Error polling events for %s: code=%s, message=%s", cachedBoxClient, statusLine.getStatusCode(), statusLine.getReasonPhrase()); } throw new RuntimeCamelException(msg); } } // keep polling retry = true; } catch (InterruptedException e) { LOG.debug("Interrupted event polling thread for {}, exiting...", cachedBoxClient); } catch (BoxSDKException e) { callback.onException(e); } catch (RuntimeCamelException e) { callback.onException(e); } catch (SocketException e) { // TODO handle connection aborts!!! LOG.debug("Socket exception while event polling for {}", cachedBoxClient); retry = true; realTimeServer = null; } catch (Exception e) { callback.onException(new RuntimeCamelException( "Error while polling for " + cachedBoxClient + ": " + e.getMessage(), e)); } finally { // are we done yet? if (!retry) { done = true; } else { if (realTimeServer != null && (++retries > maxRetries)) { // make another option call realTimeServer = null; } } } } LOG.info("Stopped event polling thread for " + cachedBoxClient); } }); } private long getCurrentStreamPosition(IBoxEventsManager eventsManager, long streamPosition) throws BoxRestException, BoxServerException, AuthFatalFailureException { final BoxEventRequestObject requestObject = BoxEventRequestObject.getEventsRequestObject(streamPosition); final BoxEventCollection events = eventsManager.getEvents(requestObject); streamPosition = events.getNextStreamPosition(); return streamPosition; } public void stopPolling() throws Exception { if (!done) { // done polling done = true; // make sure an HTTP GET is not in progress if (httpGet != null && !httpGet.isAborted()) { httpGet.abort(); } // cancel polling thread if (pollFuture.cancel(true)) { LOG.info("Stopped event polling for " + cachedBoxClient); } else { LOG.warn("Unable to stop event polling for " + cachedBoxClient); } httpClient = null; pollFuture = null; } } private BoxRealTimeServer getBoxRealTimeServer(long currentStreamPosition, IBoxEventsManager eventsManager) throws BoxRestException, BoxServerException, AuthFatalFailureException { final BoxEventRequestObject optionsRequest = BoxEventRequestObject .getEventsRequestObject(currentStreamPosition); final BoxCollection eventOptions = eventsManager.getEventOptions(optionsRequest); final ArrayList<BoxTypedObject> entries = eventOptions.getEntries(); // validate options if (entries == null || entries.size() < 1 || !(entries.get(0) instanceof BoxRealTimeServer)) { throw new RuntimeCamelException("No Real Time Server from event options for " + cachedBoxClient); } return (BoxRealTimeServer) entries.get(0); } private HttpGet getPollRequest(String url, long currentStreamPosition) throws AuthFatalFailureException { final HttpGet httpGet = new HttpGet(url + "&stream_position=" + currentStreamPosition); final String accessToken = cachedBoxClient.getBoxClient().getAuthData().getAccessToken(); httpGet.setHeader(HttpHeaders.AUTHORIZATION, "Bearer " + accessToken); return httpGet; } }