Package com.aerospike.client.async

Source Code of com.aerospike.client.async.AsyncCommand

/*
* Copyright 2012-2014 Aerospike, Inc.
*
* Portions may be licensed to Aerospike, Inc. under one or more contributor
* license agreements.
*
* 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 com.aerospike.client.async;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.concurrent.atomic.AtomicBoolean;

import com.aerospike.client.AerospikeException;
import com.aerospike.client.command.AdminCommand;
import com.aerospike.client.command.Command;
import com.aerospike.client.policy.Policy;
import com.aerospike.client.util.ThreadLocalData;
import com.aerospike.client.util.Util;

/**
* Asynchronous command handler.
*/
public abstract class AsyncCommand extends Command implements Runnable {
 
  protected AsyncConnection conn;
  protected ByteBuffer byteBuffer;
  protected final AsyncCluster cluster;
  protected AsyncNode node;
  private final AtomicBoolean complete = new AtomicBoolean();
  private long limit;
  protected int timeout;
  private int iteration;
  protected boolean inAuthenticate;
  protected boolean inHeader = true;
 
  public AsyncCommand(AsyncCluster cluster) {
    this.cluster = cluster;
  }
 
  public void execute() throws AerospikeException {
    Policy policy = getPolicy();
    timeout = policy.timeout;
   
    if (timeout > 0) { 
      limit = System.currentTimeMillis() + timeout;
    }
   
    byteBuffer = cluster.getByteBuffer();
    executeCommand();
  }
   
  public void executeCommand() throws AerospikeException {
    if (complete.get()) {
      return;
    }

    try {
      node = getNode();
      conn = node.getAsyncConnection();
     
      if (conn == null) {
        conn = new AsyncConnection(node.getAddress(), cluster);
     
        if (cluster.getUser() != null) {
          inAuthenticate = true;
          dataBuffer = ThreadLocalData.getBuffer();
          AdminCommand command = new AdminCommand(dataBuffer);
          dataOffset = command.setAuthenticate(cluster.getUser(), cluster.getPassword());
          byteBuffer.clear();
          byteBuffer.put(dataBuffer, 0, dataOffset);
          byteBuffer.flip();
          conn.execute(this);
          return;
        }
      }
      sendCommand();
    }
    catch (AerospikeException.InvalidNode ai) {
      if (!retryOnInit()) {       
        throw ai;
      }
    }
    catch (AerospikeException.Connection ce) {
      // Socket connection error has occurred.
      if (!retryOnInit()) {       
        throw ce;
      }
    }
    catch (Exception e) {
      if (!failOnApplicationInit()) {
        throw new AerospikeException(e);
      }
    }
  }
 
  protected void sendCommand() throws AerospikeException
    writeBuffer();
   
    if (dataOffset > byteBuffer.capacity()) {
      byteBuffer = ByteBuffer.allocateDirect(dataOffset);
    }
   
    byteBuffer.clear();
    byteBuffer.put(dataBuffer, 0, dataOffset);
    byteBuffer.flip();

    conn.execute(this);
  }
 
  protected void processAuthenticate() throws AerospikeException
    inAuthenticate = false;
    inHeader = true;
   
    int resultCode = byteBuffer.get(1) & 0xFF;
 
    if (resultCode != 0) {
      throw new AerospikeException(resultCode);
    }
    sendCommand();
  }

  private boolean retryOnInit() throws AerospikeException {
    if (complete.get()) {
      return true;
    }

    Policy policy = getPolicy();
   
    if (++iteration > policy.maxRetries) {
      return failOnNetworkInit();
    }

    if (limit > 0 && System.currentTimeMillis() + policy.sleepBetweenRetries > limit) {
      // Might as well stop here because the transaction will
      // timeout after sleep completed.
      return failOnNetworkInit();
    }

    // Prepare for retry.
    resetConnection();

    if (policy.sleepBetweenRetries > 0) {
      Util.sleep(policy.sleepBetweenRetries);
    }

    // Retry command recursively.
    executeCommand();
    return true;
  }

  protected final void write() throws IOException {
    conn.write(byteBuffer);
  }

  protected final void retryAfterInit(AerospikeException ae) {
    if (complete.get()) {
      return;
    }

    Policy policy = getPolicy();

    if (++iteration > policy.maxRetries) {
      failOnNetworkError(ae);
      return;
    }

    if (limit > 0 && System.currentTimeMillis() + policy.sleepBetweenRetries > limit) {
      // Might as well stop here because the transaction will
      // timeout after sleep completed.
      failOnNetworkError(ae);
      return;
    }

    // Prepare for retry.
    resetConnection();

    if (policy.sleepBetweenRetries > 0) {
      Util.sleep(policy.sleepBetweenRetries);
    }

    try {
      // Retry command recursively.
      executeCommand();
    }
    catch (Exception e) {
      // Command has already been cleaned up.
      // Notify user of original exception.
      onFailure(ae);
    }
  }

  private void resetConnection() {
    if (node != null) {
      node.decreaseHealth();
    }

    if (limit > 0) {
      // A lock on reset is required when a client timeout is specified.
      synchronized (this) {
        if (conn != null) {
          conn.close();
          conn = null;
        }
      }
    }
    else {
      if (conn != null) {
        conn.close();
        conn = null;
      }
    }
  }

  protected final boolean checkTimeout() {
    if (complete.get()) {
      return false;
    }
   
    if (limit > 0 && System.currentTimeMillis() > limit) {
      // Command has timed out in timeout queue thread.
      // Ensure that command succeeds or fails, but not both.
      if (complete.compareAndSet(false, true)) {
        // Timeout thread may contend with retry thread.
        // Lock before closing.
        synchronized (this) {
          if (conn != null) {
            conn.close();
          }
        }
        failOnClientTimeout();
      }
      return false// Do not put back on timeout queue.
    }
    return true;
  }
 
  public void run() {
    try {
      read();
     
      if (! complete.get()) {
        conn.setReadable();
      }
    }
        catch (AerospikeException.Connection ac) {
          retryAfterInit(ac);
        }
        catch (AerospikeException ae) {
      // Fail without retry on non-network errors.
      failOnApplicationError(ae);
        }
        catch (IOException ioe) {
          retryAfterInit(new AerospikeException(ioe));
        }
        catch (Exception e) {
      // Fail without retry on unknown errors.
      failOnApplicationError(new AerospikeException(e));
        }
  }
 
  protected final void finish() {
    // Finish could be called from a separate asyncTaskThreadPool thread.
    // Make sure SelectorManager thread has not already caused a transaction timeout.
    if (complete.compareAndSet(false, true)) {     
      conn.unregister();
      conn.updateLastUsed();
      node.putAsyncConnection(conn);
      node.restoreHealth();
      cluster.putByteBuffer(byteBuffer);
      onSuccess();
    }
  }

  private boolean failOnNetworkInit() {
    // Ensure that command succeeds or fails, but not both.
    if (complete.compareAndSet(false, true)) {     
      closeOnNetworkError();
      return false;
    }
    else {
      return true;
    }
  }

  private boolean failOnApplicationInit() {
    // Ensure that command succeeds or fails, but not both.
    if (complete.compareAndSet(false, true)) {     
      close();
      return false;
    }
    else {
      return true;
    }
  }

  private void failOnNetworkError(AerospikeException ae) {
    // Ensure that command succeeds or fails, but not both.
    if (complete.compareAndSet(false, true)) {     
      closeOnNetworkError();
      onFailure(ae);
    }
  }

  protected final void failOnApplicationError(AerospikeException ae) {
    // Ensure that command succeeds or fails, but not both.
    if (complete.compareAndSet(false, true)) {
      if (ae.keepConnection()) {
        // Put connection back in pool.
        conn.unregister();
        conn.updateLastUsed();
        node.putAsyncConnection(conn);
        node.restoreHealth();
        cluster.putByteBuffer(byteBuffer);
      }
      else {
        // Close socket to flush out possible garbage.
        close();
      }
      onFailure(ae);
    }
  }

  private void failOnClientTimeout() {
    // Free up resources and notify.
    closeOnNetworkError();
    onFailure(new AerospikeException.Timeout());
  }

  private void closeOnNetworkError() {
    if (node != null) {
      node.decreaseHealth();
    }
    close();
  }

  private void close() {
    // Connection was probably already closed by timeout thread.
    // Check connected status before closing again.
    if (conn != null && conn.isConnected()) {
      conn.close();
    }
    cluster.putByteBuffer(byteBuffer);
  }

  protected abstract AsyncNode getNode() throws AerospikeException.InvalidNode;
  protected abstract void read() throws AerospikeException, IOException;
  protected abstract void onSuccess();
  protected abstract void onFailure(AerospikeException ae);
}
TOP

Related Classes of com.aerospike.client.async.AsyncCommand

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.