package voldemort.store.socket.clientrequest; import java.io.DataInputStream; import java.io.IOException; import java.nio.ByteBuffer; import org.apache.log4j.Level; import org.apache.log4j.Logger; import voldemort.VoldemortException; import voldemort.common.nio.ByteBufferBackedOutputStream; import voldemort.store.StoreTimeoutException; import voldemort.store.UnreachableStoreException; import voldemort.store.nonblockingstore.NonblockingStoreCallback; import voldemort.store.socket.SocketDestination; import voldemort.store.stats.ClientSocketStats; import voldemort.utils.Time; import voldemort.utils.Utils; import voldemort.utils.pool.KeyedResourcePool; public class NonblockingStoreCallbackClientRequest<T> implements ClientRequest<T> { private final SocketDestination destination; private final ClientRequest<T> clientRequest; private final ClientRequestExecutor clientRequestExecutor; private final NonblockingStoreCallback callback; private final long startNs; private final KeyedResourcePool<SocketDestination, ClientRequestExecutor> executorPool; private final ClientSocketStats stats; private final Logger logger = Logger.getLogger(getClass()); private volatile boolean isComplete; public NonblockingStoreCallbackClientRequest(KeyedResourcePool<SocketDestination, ClientRequestExecutor> executorPool, SocketDestination destination, ClientRequest<T> clientRequest, ClientRequestExecutor clientRequestExecutor, NonblockingStoreCallback callback, ClientSocketStats stats) { this.executorPool = executorPool; this.destination = destination; this.clientRequest = clientRequest; this.clientRequestExecutor = clientRequestExecutor; this.callback = callback; this.startNs = System.nanoTime(); this.stats = stats; } private void invokeCallback(Object o, long requestTime) { if(callback != null) { try { if(logger.isDebugEnabled()) { logger.debug("Async request end; requestRef: " + System.identityHashCode(clientRequest) + " time: " + System.currentTimeMillis() + " server: " + clientRequestExecutor.getSocketChannel() .socket() .getRemoteSocketAddress() + " local socket: " + clientRequestExecutor.getSocketChannel() .socket() .getLocalAddress() + ":" + clientRequestExecutor.getSocketChannel().socket().getLocalPort() + " result: " + o); } callback.requestComplete(o, requestTime); } catch(Exception e) { if(logger.isEnabledFor(Level.WARN)) logger.warn(e, e); } } } @Override public void complete() { try { clientRequest.complete(); Object result = clientRequest.getResult(); long durationNs = Utils.elapsedTimeNs(startNs, System.nanoTime()); if(stats != null) { stats.recordAsyncOpTimeNs(destination, durationNs); } invokeCallback(result, durationNs / Time.NS_PER_MS); } catch(Exception e) { invokeCallback(e, (System.nanoTime() - startNs) / Time.NS_PER_MS); } finally { isComplete = true; // checkin may throw a (new) exception. Any prior exception // has been passed off via invokeCallback. executorPool.checkin(destination, clientRequestExecutor); } } public void reportException(IOException e) { clientRequest.reportException(e); } @Override public boolean isComplete() { return isComplete; } @Override public boolean formatRequest(ByteBufferBackedOutputStream outputStream) { return clientRequest.formatRequest(outputStream); } @Override public T getResult() throws VoldemortException, UnreachableStoreException { return clientRequest.getResult(); } @Override public boolean isCompleteResponse(ByteBuffer buffer) { return clientRequest.isCompleteResponse(buffer); } @Override public void parseResponse(DataInputStream inputStream) { clientRequest.parseResponse(inputStream); } @Override public void timeOut() { clientRequest.timeOut(); invokeCallback(new StoreTimeoutException("ClientRequestExecutor timed out for destination " + destination), (System.nanoTime() - startNs) / Time.NS_PER_MS); executorPool.checkin(destination, clientRequestExecutor); } @Override public boolean isTimedOut() { return clientRequest.isTimedOut(); } }