Package com.orientechnologies.orient.enterprise.channel.binary

Source Code of com.orientechnologies.orient.enterprise.channel.binary.OChannelBinaryAsynchClient

/*
  *
  *  *  Copyright 2014 Orient Technologies LTD (info(at)orientechnologies.com)
  *  *
  *  *  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.
  *  *
  *  * For more information: http://www.orientechnologies.com
  *
  */
package com.orientechnologies.orient.enterprise.channel.binary;

import com.orientechnologies.common.concur.OTimeoutException;
import com.orientechnologies.common.concur.lock.OLockException;
import com.orientechnologies.common.exception.OException;
import com.orientechnologies.common.log.OLogManager;
import com.orientechnologies.common.util.OPair;
import com.orientechnologies.orient.core.config.OContextConfiguration;
import com.orientechnologies.orient.core.config.OGlobalConfiguration;
import com.orientechnologies.orient.core.exception.OStorageException;
import com.orientechnologies.orient.core.serialization.OMemoryInputStream;
import com.orientechnologies.orient.enterprise.channel.OSocketFactory;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;

public class OChannelBinaryAsynchClient extends OChannelBinary {
  protected final int                          socketTimeout;                                               // IN MS
  protected final short                        srvProtocolVersion;
  private final Condition                      readCondition = getLockRead().getUnderlying().newCondition();
  private final int                            maxUnreadResponses;
  private String                               serverURL;
  private volatile boolean                     channelRead   = false;
  private byte                                 currentStatus;
  private int                                  currentSessionId;
  private volatile OAsynchChannelServiceThread serviceThread;

  public OChannelBinaryAsynchClient(final String remoteHost, final int remotePort, final String iDatabaseName,
      final OContextConfiguration iConfig, final int iProtocolVersion) throws IOException {
    this(remoteHost, remotePort, iDatabaseName, iConfig, iProtocolVersion, null);
  }

  public OChannelBinaryAsynchClient(final String remoteHost, final int remotePort, final String iDatabaseName,
      final OContextConfiguration iConfig, final int protocolVersion, final ORemoteServerEventListener asynchEventListener)
      throws IOException {
    super(OSocketFactory.instance(iConfig).createSocket(), iConfig);

    maxUnreadResponses = OGlobalConfiguration.NETWORK_BINARY_READ_RESPONSE_MAX_TIMES.getValueAsInteger();
    serverURL = remoteHost + ":" + remotePort;
    if (iDatabaseName != null)
      serverURL += "/" + iDatabaseName;
    socketTimeout = iConfig.getValueAsInteger(OGlobalConfiguration.NETWORK_SOCKET_TIMEOUT);

    socket.setPerformancePreferences(0, 2, 1);

    socket.setKeepAlive(true);
    socket.setSendBufferSize(socketBufferSize);
    socket.setReceiveBufferSize(socketBufferSize);
    try {
      socket.connect(new InetSocketAddress(remoteHost, remotePort), socketTimeout);
      setReadResponseTimeout();
      connected();
    } catch (java.net.SocketTimeoutException e) {
      throw new IOException("Cannot connect to host " + remoteHost + ":" + remotePort, e);
    }

    inStream = new BufferedInputStream(socket.getInputStream(), socketBufferSize);
    outStream = new BufferedOutputStream(socket.getOutputStream(), socketBufferSize);

    in = new DataInputStream(inStream);
    out = new DataOutputStream(outStream);

    try {
      srvProtocolVersion = readShort();
    } catch (IOException e) {
      throw new ONetworkProtocolException("Cannot read protocol version from remote server " + socket.getRemoteSocketAddress()
          + ": " + e);
    }

    if (srvProtocolVersion != protocolVersion) {
      OLogManager.instance().warn(
          this,
          "The Client driver version is different than Server version: client=" + protocolVersion + ", server="
              + srvProtocolVersion
              + ". You could not use the full features of the newer version. Assure to have the same versions on both");
    }

    if (asynchEventListener != null)
      serviceThread = new OAsynchChannelServiceThread(asynchEventListener, this);
  }

  @SuppressWarnings("unchecked")
  private static RuntimeException createException(final String iClassName, final String iMessage, final Exception iPrevious) {
    RuntimeException rootException = null;
    Constructor<?> c = null;
    try {
      final Class<RuntimeException> excClass = (Class<RuntimeException>) Class.forName(iClassName);
      if (iPrevious != null) {
        try {
          c = excClass.getConstructor(String.class, Throwable.class);
        } catch (NoSuchMethodException e) {
          c = excClass.getConstructor(String.class, Exception.class);
        }
      }

      if (c == null)
        c = excClass.getConstructor(String.class);

    } catch (Exception e) {
      // UNABLE TO REPRODUCE THE SAME SERVER-SIZE EXCEPTION: THROW A STORAGE EXCEPTION
      rootException = new OStorageException(iMessage, iPrevious);
    }

    if (c != null)
      try {
        final Throwable e;
        if (c.getParameterTypes().length > 1)
          e = (Throwable) c.newInstance(iMessage, iPrevious);
        else
          e = (Throwable) c.newInstance(iMessage);

        if (e instanceof RuntimeException)
          rootException = (RuntimeException) e;
        else
          rootException = new OException(e);
      } catch (InstantiationException ignored) {
      } catch (IllegalAccessException ignored) {
      } catch (InvocationTargetException ignored) {
      }

    return rootException;
  }

  public void beginRequest() {
    acquireWriteLock();
  }

  public void endRequest() throws IOException {
    flush();
    releaseWriteLock();
  }

  public void beginResponse(final int iRequesterId) throws IOException {
    beginResponse(iRequesterId, timeout);
  }

  public void beginResponse(final int iRequesterId, final long iTimeout) throws IOException {
    try {
      int unreadResponse = 0;
      final long startClock = iTimeout > 0 ? System.currentTimeMillis() : 0;

      // WAIT FOR THE RESPONSE
      do {
        if (iTimeout <= 0)
          acquireReadLock();
        else if (!getLockRead().tryAcquireLock(iTimeout, TimeUnit.MILLISECONDS))
          throw new OTimeoutException("Cannot acquire read lock against channel: " + this);

        boolean readLock = true;

        if (!isConnected()) {
          releaseReadLock();
          readLock = false;

          throw new IOException("Channel is closed");
        }

        if (!channelRead) {
          channelRead = true;

          try {
            setWaitResponseTimeout();
            currentStatus = readByte();
            currentSessionId = readInt();

            if (debug)
              OLogManager.instance().debug(this, "%s - Read response: %d-%d", socket.getLocalAddress(), (int) currentStatus,
                  currentSessionId);

          } catch (IOException e) {
            // UNLOCK THE RESOURCE AND PROPAGATES THE EXCEPTION
            channelRead = false;
            readCondition.signalAll();
            releaseReadLock();
            readLock = false;

            throw e;
          } finally {
            setReadResponseTimeout();
          }
        }

        if (currentSessionId == iRequesterId)
          // IT'S FOR ME
          break;

        try {
          if (debug)
            OLogManager.instance().debug(this, "%s - Session %d skip response, it is for %d", socket.getLocalAddress(),
                iRequesterId, currentSessionId);

          if (iTimeout > 0 && (System.currentTimeMillis() - startClock) > iTimeout) {
            // CLOSE THE SOCKET TO CHANNEL TO AVOID FURTHER DIRTY DATA
            close();
            readLock = false;

            throw new OTimeoutException("Timeout on reading response from the server "
                + (socket != null ? socket.getRemoteSocketAddress() : "") + " for the request " + iRequesterId);
          }

          if (unreadResponse > maxUnreadResponses) {
            if (debug)
              OLogManager.instance().info(this, "Unread responses %d > %d, consider the buffer as dirty: clean it", unreadResponse,
                  maxUnreadResponses);

            close();
            readLock = false;

            throw new IOException("Timeout on reading response");
          }

          readCondition.signalAll();

          if (debug)
            OLogManager.instance().debug(this, "Session %d is going to sleep...", iRequesterId);

          final long start = System.currentTimeMillis();

          // WAIT 1 SECOND AND RETRY
          readCondition.await(1, TimeUnit.SECONDS);
          final long now = System.currentTimeMillis();

          if (debug)
            OLogManager.instance().debug(this, "Waked up: slept %dms, checking again from %s for session %d", (now - start),
                socket.getLocalAddress(), iRequesterId);

          if (now - start >= 1000)
            unreadResponse++;

        } catch (InterruptedException e) {
          Thread.currentThread().interrupt();

        } finally {
          if (readLock)
            releaseReadLock();
        }
      } while (true);

      if (debug)
        OLogManager.instance().debug(this, "%s - Session %d handle response", socket.getLocalAddress(), iRequesterId);

      handleStatus(currentStatus, currentSessionId);
    } catch (OLockException e) {
      Thread.currentThread().interrupt();
      // NEVER HAPPENS?
      OLogManager.instance().error(this, "Unexpected error on reading response from channel", e);
    }
  }

  private void setReadResponseTimeout() throws SocketException {
    if (socket != null)
      socket.setSoTimeout(socketTimeout);
  }

  private void setWaitResponseTimeout() throws SocketException {
    if (socket != null)
      socket.setSoTimeout(OGlobalConfiguration.NETWORK_REQUEST_TIMEOUT.getValueAsInteger());
  }

  public void endResponse() {
    channelRead = false;

    // WAKE UP ALL THE WAITING THREADS
    try {
      readCondition.signalAll();
    } catch (IllegalMonitorStateException e) {
      // IGNORE IT
      OLogManager.instance().debug(this, "Error on signaling waiting clients after reading response");
    }

    try {
      releaseReadLock();
    } catch (IllegalMonitorStateException e) {
      // IGNORE IT
      OLogManager.instance().debug(this, "Error on unlocking network channel after reading response");
    }
  }

  @Override
  public void close() {
    if (getLockRead().tryAcquireLock())
      try {
        readCondition.signalAll();
      } finally {
        releaseReadLock();
      }

    super.close();

    if (serviceThread != null) {
      final OAsynchChannelServiceThread s = serviceThread;
      serviceThread = null;
      if (s != null)
        // CHECK S BECAUSE IT COULD BE CONCURRENTLY RESET
        s.sendShutdown();
    }
  }

  @Override
  public void clearInput() throws IOException {
    acquireReadLock();
    try {
      super.clearInput();
    } finally {
      releaseReadLock();
    }
  }

  /**
   * Tells if the channel is connected.
   *
   * @return true if it's connected, otherwise false.
   */
  public boolean isConnected() {
    final Socket s = socket;
    return s != null && !s.isClosed() && s.isConnected() && !s.isInputShutdown() && !s.isOutputShutdown();
  }

  /**
   * Gets the major supported protocol version
   *
   */
  public short getSrvProtocolVersion() {
    return srvProtocolVersion;
  }

  public String getServerURL() {
    return serverURL;
  }

  public boolean tryLock() {
    return getLockWrite().tryAcquireLock();
  }

  public void unlock() {
    getLockWrite().unlock();
  }

  protected int handleStatus(final byte iResult, final int iClientTxId) throws IOException {
    if (iResult == OChannelBinaryProtocol.RESPONSE_STATUS_OK || iResult == OChannelBinaryProtocol.PUSH_DATA) {
      return iClientTxId;
    } else if (iResult == OChannelBinaryProtocol.RESPONSE_STATUS_ERROR) {

      final List<OPair<String, String>> exceptions = new ArrayList<OPair<String, String>>();

      // EXCEPTION
      while (readByte() == 1) {
        final String excClassName = readString();
        final String excMessage = readString();
        exceptions.add(new OPair<String, String>(excClassName, excMessage));
      }

      byte[] serializedException = null;
      if (srvProtocolVersion >= 19)
        serializedException = readBytes();

      Exception previous = null;

      if (serializedException != null && serializedException.length > 0)
        throwSerializedException(serializedException);

      for (int i = exceptions.size() - 1; i > -1; --i) {
        previous = createException(exceptions.get(i).getKey(), exceptions.get(i).getValue(), previous);
      }

      if (previous != null) {
        throw new RuntimeException(previous);
      } else
        throw new ONetworkProtocolException("Network response error");

    } else {
      // PROTOCOL ERROR
      // close();
      throw new ONetworkProtocolException("Error on reading response from the server");
    }
  }

  private void throwSerializedException(final byte[] serializedException) throws IOException {
    final OMemoryInputStream inputStream = new OMemoryInputStream(serializedException);
    final ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);

    Object throwable = null;
    try {
      throwable = objectInputStream.readObject();
    } catch (ClassNotFoundException e) {
      OLogManager.instance().error(this, "Error during exception serialization.", e);
    }

    objectInputStream.close();

    if (throwable instanceof OException)
      throw (OException) throwable;
    else if (throwable instanceof Throwable)
      // WRAP IT
      throw new OResponseProcessingException("Exception during response processing.", (Throwable) throwable);
    else
      OLogManager.instance().error(
          this,
          "Error during exception serialization, serialized exception is not Throwable, exception type is "
              + (throwable != null ? throwable.getClass().getName() : "null"));
  }
}
TOP

Related Classes of com.orientechnologies.orient.enterprise.channel.binary.OChannelBinaryAsynchClient

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.