/* * Copyright 2009 Thomas Bocek * * 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 net.tomp2p.futures; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Semaphore; import net.tomp2p.connection2.ProgresHandler; import net.tomp2p.message.Message2; /** * Each response has one request messages. The corresponding response message is set only if the request has been * successful. This is indicated with isFailed. * * @author Thomas Bocek */ public class FutureResponse extends BaseFutureImpl<FutureResponse> { // the message that was requested private final Message2 requestMessage; private final FutureSuccessEvaluator futureSuccessEvaluator; // the reply to this request private Message2 responseMessage; private ProgresHandler progressHandler; private final ProgressListener progressListener; private final CountDownLatch firstProgressHandler = new CountDownLatch(1); private final CountDownLatch secondProgressHandler = new CountDownLatch(1); /** * Create the future and set the request message. * * @param requestMessage * The request message that will be send over the wire. */ public FutureResponse(final Message2 requestMessage) { this(requestMessage, new FutureSuccessEvaluatorCommunication()); } /** * Create the future and set the request message. * * @param requestMessage * The request message that will be send over the wire. * @param futureSuccessEvaluator * Evaluates if the future was a success or failure */ public FutureResponse(final Message2 requestMessage, final FutureSuccessEvaluator futureSuccessEvaluator) { this(requestMessage, futureSuccessEvaluator, null); } /** * Create the future and set the request message. This will set the progress listener for streaming support. * * @param requestMessage * The request message that will be send over the wire. * @param progressListener * The progress listener for streaming support */ public FutureResponse(final Message2 requestMessage, final ProgressListener progressListener) { this(requestMessage, new FutureSuccessEvaluatorCommunication(), progressListener); } /** * Create the future and set the request message. This will set the progress listener for streaming support. * * @param requestMessage * The request message that will be send over the wire. * @param futureSuccessEvaluator * Evaluates if the future was a success or failure * @param progressListener * The progress listener for streaming support */ public FutureResponse(final Message2 requestMessage, final FutureSuccessEvaluator futureSuccessEvaluator, final ProgressListener progressListener) { this.requestMessage = requestMessage; this.futureSuccessEvaluator = futureSuccessEvaluator; this.progressListener = progressListener; self(this); } /** * If we don't get a reply message, which is the case for fire-and-forget messages, then set the reply to null and * set this future to complete with the type Success. * * @return This class */ public FutureResponse setResponse() { return setResponse(null); } /** * Gets called if a peer responds. Note that either this method or responseFailed() is always called. This does not * notify any listeners. The listeners gets notified if channel is closed * * @param responseMessage * The received message * @return This class */ public FutureResponse setResponse(final Message2 responseMessage) { synchronized (lock) { if (!setCompletedAndNotify()) { return this; } if (responseMessage != null) { this.responseMessage = responseMessage; // if its ok or nok, the communication was successful. // Everything else is a failure in communication type = futureSuccessEvaluator.evaluate(requestMessage, responseMessage); reason = responseMessage.getType().toString(); } else { type = FutureType.OK; reason = "Nothing to deliver..."; } } notifyListerenrs(); return this; } @Override public FutureResponse setFailed(final String reason) { synchronized (lock) { if (!setCompletedAndNotify()) { return this; } this.reason = reason; this.type = FutureType.FAILED; } notifyListerenrs(); return this; } /** * Returns the response message. This is the same message as in response(Message message). If no response where * send, then this will return null. * * @return The successful response message or null if failed */ public Message2 getResponse() { synchronized (lock) { return responseMessage; } } /** * The future response always keeps a reference to the request. * * @return The request message. */ public Message2 getRequest() { synchronized (lock) { return requestMessage; } } /** * Set the user based progres handler, where the user can add more data and call {@link #progress()} when data has * been added. * * @param progressHandler * The progress handler that will be added by the TomP2P library. * @return This class */ public FutureResponse setProgressHandler(final ProgresHandler progressHandler) { synchronized (lock) { this.progressHandler = progressHandler; } firstProgressHandler.countDown(); return this; } /** * Do not call this directly, as this is called from the TomP2P library. This must be the first call to the * progress handler, otherwise we might not be able to decode the header. * * @return This class * @throws InterruptedException . */ public FutureResponse progressFirst() throws InterruptedException { firstProgressHandler.await(); synchronized (lock) { progressHandler.progres(); } secondProgressHandler.countDown(); return this; } /** * This will be called by the user, when the user provides more data. This will block until a progress handler has * been set by the TomP2P library. * * @return This class * @throws InterruptedException * If latch is interrupted */ public FutureResponse progress() throws InterruptedException { secondProgressHandler.await(); synchronized (lock) { progressHandler.progres(); } return this; } /** * This will be called by the TomP2P library when a partial message is ready. * * @param interMediateMessage * The message that is either incomplete as indicated with {@link Message2#isDone()} or completed. This * will be called before the future completes. */ public void progress(final Message2 interMediateMessage) { synchronized (lock) { if (progressListener != null) { progressListener.progress(interMediateMessage); } } } }