/* * 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.io.PrintWriter; import java.io.StringWriter; import java.util.Map; import net.tomp2p.message.Buffer; import net.tomp2p.message.DataMap; import net.tomp2p.message.Message; import net.tomp2p.message.TrackerData; import net.tomp2p.peers.Number640; import net.tomp2p.peers.PeerAddress; import net.tomp2p.peers.RTT; import net.tomp2p.storage.Data; /** * 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 Message requestMessage; private final FutureSuccessEvaluator futureSuccessEvaluator; // the reply to this request protected Message responseMessage; protected boolean responseLater = false; private final RTT roundTripTime = new RTT(); /** * Creates a future and sets the request message. * * @param requestMessage * The request message that will be sent. */ public FutureResponse(final Message requestMessage) { this(requestMessage, new FutureSuccessEvaluatorCommunication()); } /** * Creates a future and sets the request message. * * @param requestMessage * The request message that will be sent. * @param futureSuccessEvaluator * Evaluates if the future was a success or a failure. */ public FutureResponse(final Message requestMessage, final FutureSuccessEvaluator futureSuccessEvaluator) { this.requestMessage = requestMessage; this.futureSuccessEvaluator = futureSuccessEvaluator; 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 emptyResponse() { return response(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 response(final Message responseMessage) { synchronized (lock) { if (!completedAndNotify()) { if(responseMessage != null) { responseMessage.release(); } 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.type().toString(); } else { type = FutureType.OK; reason = "Nothing to deliver..."; } } notifyListeners(); return this; } /*public boolean responseLater(final Message responseMessage) { synchronized (lock) { if(completed) { if(responseMessage != null) { responseMessage.release(); } return false; } responseLater = true; 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.type().toString(); } else { type = FutureType.OK; reason = "Nothing to deliver..."; } } return true; } public boolean failedLater(final Throwable cause) { StringWriter stringWriter = new StringWriter(); PrintWriter printWriter = new PrintWriter(stringWriter); cause.printStackTrace(printWriter); synchronized (lock) { if(completed) { return false; } responseLater = true; this.reason = stringWriter.toString(); this.type = FutureType.FAILED; } return true; } public FutureResponse responseNow() { synchronized (lock) { if (!responseLater && !completed) { failed("No future set beforehand, probably an early shutdown / timeout, or use setFailedLater() or setResponseLater()"); return this; } if (!super.completedAndNotify()) { return this; } } notifyListeners(); return this; }*/ protected boolean completedAndNotify() { if (responseLater) { return false; } return super.completedAndNotify(); } /** * 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 Message responseMessage() { synchronized (lock) { return responseMessage; } } /** * The future response always keeps a reference to the request. * * @return The request message. */ public Message request() { synchronized (lock) { return requestMessage; } } /** * Start measuring time until reply is recieved * * @return True if time has successfully been started * False if measurement already has been stopped */ public boolean startRTTMeasurement(boolean isUDP) { return getRoundTripTime().beginTimeMeasurement(isUDP); } /** * Stops time measurement for the round trip time * * @return True if some valid time was recorded, False if time has not yet * been started or already been stopped */ public boolean stopRTTMeasurement() { return getRoundTripTime().stopTimeMeasurement(); } @Override public String toString() { StringBuilder sb = new StringBuilder("future response state:"); sb.append(",type:").append(type.name()).append(",msg:").append(requestMessage.command()).append(",reason:").append(reason); return sb.toString(); } public RTT getRoundTripTime() { return roundTripTime; } public FutureResponse releaseResponseMessage() { releaseMessage(responseMessage); return this; } public FutureResponse releaseRequestMessage() { releaseMessage(requestMessage); return this; } public FutureResponse release() { releaseMessage(responseMessage); releaseMessage(requestMessage); return this; } private FutureResponse releaseMessage(Message message) { if(message != null) { for(Buffer buffer:message.bufferList()) { buffer.buffer().release(); } for(DataMap dataMap:message.dataMapList()) { for(Map.Entry<Number640, Data> entry: dataMap.dataMap().entrySet()) { entry.getValue().release(); } } for(TrackerData trackerData:message.trackerDataList()) { for(Map.Entry<PeerAddress, Data> entry: trackerData.peerAddresses().entrySet()) { entry.getValue().release(); } } } return this; } public FutureSuccessEvaluator futureSuccessEvaluator() { return futureSuccessEvaluator; } }