/* * Copyright (c) 2010-2012 Sonatype, Inc. All rights reserved. * * This program is licensed to you under the Apache License Version 2.0, * and you may not use this file except in compliance with the Apache License Version 2.0. * You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0. * * Unless required by applicable law or agreed to in writing, * software distributed under the Apache License Version 2.0 is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the Apache License Version 2.0 for the specific language governing permissions and limitations there under. */ package com.ning.http.client.providers.jdk; import java.net.HttpURLConnection; import java.util.concurrent.Callable; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; import com.ning.http.client.AsyncHandler; import com.ning.http.client.listenable.AbstractListenableFuture; public class JDKFuture<V> extends AbstractListenableFuture<V> { protected Future<V> innerFuture; protected final AsyncHandler<V> asyncHandler; protected final int responseTimeoutInMs; protected final AtomicBoolean cancelled = new AtomicBoolean(false); protected final AtomicBoolean timedOut = new AtomicBoolean(false); protected final AtomicBoolean isDone = new AtomicBoolean(false); protected final AtomicReference<Throwable> exception = new AtomicReference<Throwable>(); protected final AtomicLong touch = new AtomicLong( System.currentTimeMillis()); protected final AtomicBoolean contentProcessed = new AtomicBoolean(false); protected final HttpURLConnection urlConnection; private boolean writeHeaders; private boolean writeBody; public JDKFuture(final AsyncHandler<V> asyncHandler, final int responseTimeoutInMs, final HttpURLConnection urlConnection) { this.asyncHandler = asyncHandler; this.responseTimeoutInMs = responseTimeoutInMs; this.urlConnection = urlConnection; writeHeaders = true; writeBody = true; } protected void setInnerFuture(final Future<V> innerFuture) { this.innerFuture = innerFuture; } @Override public void done(final Callable callable) { isDone.set(true); super.done(); } @Override public void abort(final Throwable t) { exception.set(t); if (innerFuture != null) { innerFuture.cancel(true); } if (!timedOut.get() && !cancelled.get()) { try { asyncHandler.onThrowable(t); } catch (final Throwable te) { } } super.done(); } @Override public void content(final V v) { } @Override public boolean cancel(final boolean mayInterruptIfRunning) { if (!cancelled.get() && innerFuture != null) { urlConnection.disconnect(); try { asyncHandler.onThrowable(new CancellationException()); } catch (final Throwable te) { } cancelled.set(true); super.done(); return innerFuture.cancel(mayInterruptIfRunning); } else { super.done(); return false; } } @Override public boolean isCancelled() { if (innerFuture != null) { return innerFuture.isCancelled(); } else { return false; } } @Override public boolean isDone() { contentProcessed.set(true); return innerFuture.isDone(); } @Override public V get() throws InterruptedException, ExecutionException { try { return get(responseTimeoutInMs, TimeUnit.MILLISECONDS); } catch (final TimeoutException e) { throw new ExecutionException(e); } } @Override public V get(final long timeout, final TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { V content = null; try { if (innerFuture != null) { content = innerFuture.get(timeout, unit); } } catch (final TimeoutException t) { if (!contentProcessed.get() && timeout != -1 && ((System.currentTimeMillis() - touch.get()) <= responseTimeoutInMs)) { return get(timeout, unit); } if (exception.get() == null) { timedOut.set(true); throw new ExecutionException(new TimeoutException( String.format("No response received after %s", responseTimeoutInMs))); } } catch (final CancellationException ce) { } if (exception.get() != null) { throw new ExecutionException(exception.get()); } return content; } /** * Is the Future still valid * * @return <code>true</code> if response has expired and should be * terminated. */ public boolean hasExpired() { return responseTimeoutInMs != -1 && ((System.currentTimeMillis() - touch.get()) > responseTimeoutInMs); } /** * {@inheritDoc} */ /* @Override */ @Override public void touch() { touch.set(System.currentTimeMillis()); } /** * {@inheritDoc} */ /* @Override */ @Override public boolean getAndSetWriteHeaders(final boolean writeHeaders) { final boolean b = this.writeHeaders; this.writeHeaders = writeHeaders; return b; } /** * {@inheritDoc} */ /* @Override */ @Override public boolean getAndSetWriteBody(final boolean writeBody) { final boolean b = this.writeBody; this.writeBody = writeBody; return b; } }