Package com.orientechnologies.orient.client.remote

Source Code of com.orientechnologies.orient.client.remote.OStorageRemote

/*
* Copyright 1999-2010 Luca Garulli (l.garulli--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.
*/
package com.orientechnologies.orient.client.remote;

import java.io.IOException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import com.orientechnologies.common.exception.OException;
import com.orientechnologies.common.io.OIOException;
import com.orientechnologies.common.log.OLogManager;
import com.orientechnologies.common.profiler.OProfiler;
import com.orientechnologies.common.util.OPair;
import com.orientechnologies.orient.core.Orient;
import com.orientechnologies.orient.core.command.OCommandRequestAsynch;
import com.orientechnologies.orient.core.command.OCommandRequestText;
import com.orientechnologies.orient.core.config.OContextConfiguration;
import com.orientechnologies.orient.core.config.OGlobalConfiguration;
import com.orientechnologies.orient.core.config.OStorageConfiguration;
import com.orientechnologies.orient.core.db.record.ODatabaseRecord;
import com.orientechnologies.orient.core.db.record.OIdentifiable;
import com.orientechnologies.orient.core.exception.OCommandExecutionException;
import com.orientechnologies.orient.core.exception.ODatabaseException;
import com.orientechnologies.orient.core.exception.OStorageException;
import com.orientechnologies.orient.core.id.ORecordId;
import com.orientechnologies.orient.core.record.ORecord;
import com.orientechnologies.orient.core.record.ORecordInternal;
import com.orientechnologies.orient.core.record.ORecordSchemaAware;
import com.orientechnologies.orient.core.record.impl.ODocument;
import com.orientechnologies.orient.core.serialization.OSerializableStream;
import com.orientechnologies.orient.core.serialization.serializer.record.string.ORecordSerializerStringAbstract;
import com.orientechnologies.orient.core.serialization.serializer.stream.OStreamSerializerAnyStreamable;
import com.orientechnologies.orient.core.storage.OCluster;
import com.orientechnologies.orient.core.storage.ORawBuffer;
import com.orientechnologies.orient.core.storage.OStorage;
import com.orientechnologies.orient.core.storage.OStorageAbstract;
import com.orientechnologies.orient.core.tx.OTransaction;
import com.orientechnologies.orient.core.tx.OTransactionAbstract;
import com.orientechnologies.orient.core.tx.OTransactionRecordEntry;
import com.orientechnologies.orient.enterprise.channel.binary.OChannelBinaryClient;
import com.orientechnologies.orient.enterprise.channel.binary.OChannelBinaryProtocol;
import com.orientechnologies.orient.enterprise.channel.binary.ONetworkProtocolException;

/**
* This object is bound to each remote ODatabase instances.
*/
public class OStorageRemote extends OStorageAbstract {
  private static final String                      DEFAULT_HOST                = "localhost";
  private static final String[]                    DEFAULT_PORTS                = new String[] { "2424" };
  private static final String                      ADDRESS_SEPARATOR            = ";";

  public static final String                      PARAM_MIN_POOL              = "minpool";
  public static final String                      PARAM_MAX_POOL              = "maxpool";

  private OStorageRemoteServiceThread              serviceThread;
  private OContextConfiguration                    clientConfiguration;
  private int                                      connectionRetry;
  private int                                      connectionRetryDelay;

  private static List<OChannelBinaryClient>        networkPool                  = new ArrayList<OChannelBinaryClient>();
  protected List<OPair<String, String[]>>          serverURLs                  = new ArrayList<OPair<String, String[]>>();
  protected final Map<String, Integer>            clustersIds                  = new HashMap<String, Integer>();
  protected final Map<String, String>              clustersTypes                = new HashMap<String, String>();
  protected int                                    defaultClusterId;
  private int                                      networkPoolCursor            = 0;
  private int                                      minPool;
  private int                                      maxPool;
  private final boolean                            debug                        = false;
  private ODocument                                clusterConfiguration;
  private final List<ORemoteServerEventListener>  remoteServerEventListeners  = new ArrayList<ORemoteServerEventListener>();
  private String                                  connectionUserName;
  private String                                  connectionUserPassword;
  private Map<String, Object>                      connectionOptions;

  public OStorageRemote(final String iURL, final String iMode) throws IOException {
    super(iURL, iURL, iMode);
    configuration = null;

    clientConfiguration = new OContextConfiguration();
    connectionRetry = clientConfiguration.getValueAsInteger(OGlobalConfiguration.NETWORK_SOCKET_RETRY);
    connectionRetryDelay = clientConfiguration.getValueAsInteger(OGlobalConfiguration.NETWORK_SOCKET_RETRY_DELAY);

    parseServerURLs();
  }

  public int getSessionId() {
    return OStorageRemoteThreadLocal.INSTANCE.get().sessionId.intValue();
  }

  public void setSessionId(final int iSessionId) {
    OStorageRemoteThreadLocal.INSTANCE.get().sessionId = iSessionId;
  }

  public List<ORemoteServerEventListener> getRemoteServerEventListeners() {
    return remoteServerEventListeners;
  }

  public void addRemoteServerEventListener(final ORemoteServerEventListener iListener) {
    remoteServerEventListeners.add(iListener);
  }

  public void removeRemoteServerEventListener(final ORemoteServerEventListener iListener) {
    remoteServerEventListeners.remove(iListener);
  }

  public void open(final String iUserName, final String iUserPassword, final Map<String, Object> iOptions) {
    addUser();

    lock.acquireExclusiveLock();

    connectionUserName = iUserName;
    connectionUserPassword = iUserPassword;
    connectionOptions = iOptions != null ? new HashMap<String, Object>(iOptions) : null; // CREATE A COPY TO AVOID USER MANIPULATION
                                                                                          // POST OPEN

    try {
      openRemoteDatabase();

      configuration = new OStorageConfiguration(this);
      configuration.load();

    } catch (Exception e) {
      if (!OGlobalConfiguration.STORAGE_KEEP_OPEN.getValueAsBoolean())
        close();

      if (e instanceof OException)
        // PASS THROUGH
        throw (OException) e;
      else
        throw new OStorageException("Can't open the remote storage: " + name, e);

    } finally {
      lock.releaseExclusiveLock();
    }
  }

  public void reload() {
    checkConnection();

    do {
      try {

        OChannelBinaryClient network = null;
        try {
          network = beginRequest(OChannelBinaryProtocol.REQUEST_DB_RELOAD);
        } finally {
          endRequest(network);
        }

        try {
          beginResponse(network);

          readDatabaseInformation(network);
          break;

        } finally {
          endResponse(network);
        }

      } catch (OException e) {
        // PASS THROUGH
        throw e;
      } catch (Exception e) {
        handleException("Error on reloading database information", e);

      }
    } while (true);
  }

  public void create(final Map<String, Object> iOptions) {
    throw new UnsupportedOperationException(
        "Can't create a database in a remote server. Please use the console or the OServerAdmin class.");
  }

  public boolean exists() {
    throw new UnsupportedOperationException(
        "Can't check the existance of a database in a remote server. Please use the console or the OServerAdmin class.");
  }

  public void close(final boolean iForce) {
    lock.acquireExclusiveLock();

    OChannelBinaryClient network = null;
    try {
      if (networkPool.size() > 0) {
        try {
          network = beginRequest(OChannelBinaryProtocol.REQUEST_DB_CLOSE);
        } finally {
          endRequest(network);
        }

        getResponse(network);
      }

      OStorageRemoteThreadLocal.INSTANCE.get().sessionId = -1;

      if (!checkForClose(iForce))
        return;

      // CLOSE THE CHANNEL
      if (serviceThread != null) {
        serviceThread.sendShutdown();
        serviceThread.interrupt();
      }

      synchronized (networkPool) {
        for (OChannelBinaryClient n : networkPool)
          n.close();
        networkPool.clear();
      }

      level2Cache.shutdown();
      super.close(iForce);
      status = STATUS.CLOSED;

      Orient.instance().unregisterStorage(this);

    } catch (OException e) {
      // PASS THROUGH
      throw e;
    } catch (Exception e) {
      OLogManager.instance().debug(this, "Error on closing remote connection: %s", network);
    } finally {

      lock.releaseExclusiveLock();
    }
  }

  public void delete() {
    throw new UnsupportedOperationException(
        "Can't delete a database in a remote server. Please use the console or the OServerAdmin class.");
  }

  public Set<String> getClusterNames() {
    lock.acquireSharedLock();

    try {
      checkConnection();
      return clustersIds.keySet();

    } finally {
      lock.releaseSharedLock();
    }
  }

  /**
   * Method that completes the cluster rename operation. <strong>IT WILL NOT RENAME A CLUSTER, IT JUST CHANGES THE NAME IN THE
   * INTERNAL MAPPING</strong>
   */
  public void renameCluster(String iOldName, String iNewName) {
    lock.acquireSharedLock();

    try {
      checkConnection();
      final Integer clusterId = clustersIds.remove(iOldName);
      clustersIds.put(iNewName, clusterId);

    } finally {
      lock.releaseSharedLock();
    }
  }

  public long createRecord(final ORecordId iRid, final byte[] iContent, final byte iRecordType) {
    checkConnection();

    do {
      try {

        OChannelBinaryClient network = null;
        try {
          network = beginRequest(OChannelBinaryProtocol.REQUEST_RECORD_CREATE);

          network.writeShort((short) iRid.clusterId);
          network.writeBytes(iContent);
          network.writeByte(iRecordType);

        } finally {
          endRequest(network);
        }

        try {
          beginResponse(network);

          iRid.clusterPosition = network.readLong();
          return iRid.clusterPosition;

        } finally {
          endResponse(network);
        }

      } catch (OException e) {
        // PASS THROUGH
        throw e;
      } catch (Exception e) {
        handleException("Error on create record in cluster: " + iRid.clusterId, e);

      }
    } while (true);
  }

  public ORawBuffer readRecord(final ODatabaseRecord iDatabase, final ORecordId iRid, final String iFetchPlan) {
    checkConnection();

    if (OStorageRemoteThreadLocal.INSTANCE.get().commandExecuting)
      // PENDING NETWORK OPERATION, CAN'T EXECUTE IT NOW
      return null;

    do {
      try {

        OChannelBinaryClient network = null;
        try {

          network = beginRequest(OChannelBinaryProtocol.REQUEST_RECORD_LOAD);
          network.writeRID(iRid);
          network.writeString(iFetchPlan != null ? iFetchPlan : "");

        } finally {
          endRequest(network);
        }

        try {
          beginResponse(network);

          if (network.readByte() == 0)
            return null;

          final ORawBuffer buffer = new ORawBuffer(network.readBytes(), network.readInt(), network.readByte());

          ORecordInternal<?> record;
          while (network.readByte() == 2) {
            record = (ORecordInternal<?>) readIdentifiable(network, iDatabase);

            // PUT IN THE CLIENT LOCAL CACHE
            iDatabase.getLevel1Cache().updateRecord(record);
          }
          return buffer;
        } finally {
          endResponse(network);
        }

      } catch (OException e) {
        // PASS THROUGH
        throw e;
      } catch (Exception e) {
        handleException("Error on read record #" + iRid, e);

      }
    } while (true);
  }

  public int updateRecord(final ORecordId iRid, final byte[] iContent, final int iVersion, final byte iRecordType) {
    checkConnection();

    do {
      try {
        OChannelBinaryClient network = null;
        try {
          network = beginRequest(OChannelBinaryProtocol.REQUEST_RECORD_UPDATE);

          network.writeRID(iRid);
          network.writeBytes(iContent);
          network.writeInt(iVersion);
          network.writeByte(iRecordType);

        } finally {
          endRequest(network);
        }

        try {
          beginResponse(network);
          return network.readInt();
        } finally {
          endResponse(network);
        }

      } catch (OException e) {
        // PASS THROUGH
        throw e;
      } catch (Exception e) {
        handleException("Error on update record #" + iRid, e);

      }
    } while (true);
  }

  public boolean deleteRecord(final ORecordId iRid, final int iVersion) {
    checkConnection();

    do {
      try {
        OChannelBinaryClient network = null;
        try {
          network = beginRequest(OChannelBinaryProtocol.REQUEST_RECORD_DELETE);

          network.writeRID(iRid);
          network.writeInt(iVersion);

        } finally {
          endRequest(network);
        }

        try {
          beginResponse(network);
          return network.readByte() == 1;
        } finally {
          endResponse(network);
        }

      } catch (OException e) {
        // PASS THROUGH
        throw e;
      } catch (Exception e) {
        handleException("Error on delete record #" + iRid, e);

      }
    } while (true);
  }

  public long count(final int iClusterId) {
    return count(new int[] { iClusterId });
  }

  public long[] getClusterDataRange(final int iClusterId) {
    checkConnection();

    do {
      try {
        OChannelBinaryClient network = null;
        try {
          network = beginRequest(OChannelBinaryProtocol.REQUEST_DATACLUSTER_DATARANGE);

          network.writeShort((short) iClusterId);

        } finally {
          endRequest(network);
        }

        try {
          beginResponse(network);
          return new long[] { network.readLong(), network.readLong() };
        } finally {
          endResponse(network);
        }

      } catch (OException e) {
        // PASS THROUGH
        throw e;
      } catch (Exception e) {
        handleException("Error on getting last entry position count in cluster: " + iClusterId, e);

      }
    } while (true);
  }

  public long getSize() {
    checkConnection();

    do {
      try {
        OChannelBinaryClient network = null;
        try {

          network = beginRequest(OChannelBinaryProtocol.REQUEST_DB_SIZE);

        } finally {
          endRequest(network);
        }

        try {
          beginResponse(network);
          return network.readLong();
        } finally {
          endResponse(network);
        }
      } catch (OException e) {
        // PASS THROUGH
        throw e;
      } catch (Exception e) {
        handleException("Error on read database size", e);

      }
    } while (true);
  }

  @Override
  public long countRecords() {
    checkConnection();

    do {
      try {
        OChannelBinaryClient network = null;
        try {

          network = beginRequest(OChannelBinaryProtocol.REQUEST_DB_COUNTRECORDS);

        } finally {
          endRequest(network);
        }

        try {
          beginResponse(network);
          return network.readLong();
        } finally {
          endResponse(network);
        }
      } catch (OException e) {
        // PASS THROUGH
        throw e;
      } catch (Exception e) {
        handleException("Error on read database record count", e);

      }
    } while (true);
  }

  public long count(final int[] iClusterIds) {
    checkConnection();

    do {
      try {
        OChannelBinaryClient network = null;
        try {
          network = beginRequest(OChannelBinaryProtocol.REQUEST_DATACLUSTER_COUNT);

          network.writeShort((short) iClusterIds.length);
          for (int i = 0; i < iClusterIds.length; ++i)
            network.writeShort((short) iClusterIds[i]);

        } finally {
          endRequest(network);
        }

        try {
          beginResponse(network);
          return network.readLong();
        } finally {
          endResponse(network);
        }
      } catch (OException e) {
        // PASS THROUGH
        throw e;
      } catch (Exception e) {
        handleException("Error on read record count in clusters: " + Arrays.toString(iClusterIds), e);

      }
    } while (true);
  }

  public long count(final String iClassName) {
    checkConnection();

    do {

      try {
        OChannelBinaryClient network = null;
        try {

          network = beginRequest(OChannelBinaryProtocol.REQUEST_COUNT);
          network.writeString(iClassName);

        } finally {
          endRequest(network);
        }

        try {
          beginResponse(network);
          return network.readLong();
        } finally {
          endResponse(network);
        }
      } catch (OException e) {
        // PASS THROUGH
        throw e;
      } catch (Exception e) {
        handleException("Error on executing count on class: " + iClassName, e);

      }
    } while (true);
  }

  /**
   * Execute the command remotely and get the results back.
   */
  public Object command(final OCommandRequestText iCommand) {
    checkConnection();

    if (!(iCommand instanceof OSerializableStream))
      throw new OCommandExecutionException("Can't serialize the command to being executed to the server side.");

    OSerializableStream command = iCommand;
    Object result = null;

    do {
      OStorageRemoteThreadLocal.INSTANCE.get().commandExecuting = true;

      try {
        final OCommandRequestText aquery = iCommand;

        final boolean asynch = iCommand instanceof OCommandRequestAsynch;

        OChannelBinaryClient network = null;
        try {
          network = beginRequest(OChannelBinaryProtocol.REQUEST_COMMAND);

          network.writeByte((byte) (asynch ? 'a' : 's')); // ASYNC / SYNC
          network.writeBytes(OStreamSerializerAnyStreamable.INSTANCE.toStream(iCommand.getDatabase(), command));

        } finally {
          endRequest(network);
        }

        try {
          beginResponse(network);

          if (asynch) {
            byte status;

            // ASYNCH: READ ONE RECORD AT TIME
            while ((status = network.readByte()) > 0) {
              final ORecordSchemaAware<?> record = (ORecordSchemaAware<?>) readIdentifiable(network, iCommand.getDatabase());
              if (record == null)
                break;

              switch (status) {
              case 1:
                // PUT AS PART OF THE RESULT SET. INVOKE THE LISTENER
                try {
                  if (!aquery.getResultListener().result(record)) {
                    // EMPTY THE INPUT CHANNEL
                    while (network.in.available() > 0)
                      network.in.read();

                    break;
                  }
                } catch (Throwable t) {
                  // ABSORBE ALL THE USER EXCEPTIONS
                  t.printStackTrace();
                }
                iCommand.getDatabase().getLevel1Cache().updateRecord(record);
                break;

              case 2:
                // PUT IN THE CLIENT LOCAL CACHE
                iCommand.getDatabase().getLevel1Cache().updateRecord(record);
              }
            }
          } else {
            final byte type = network.readByte();
            switch (type) {
            case 'n':
              result = null;
              break;

            case 'r':
              result = readIdentifiable(network, iCommand.getDatabase());
              if (result instanceof ORecord<?>)
                iCommand.getDatabase().getLevel1Cache().updateRecord((ORecordInternal<?>) result);
              break;

            case 'l':
              final int tot = network.readInt();
              final Collection<OIdentifiable> list = new ArrayList<OIdentifiable>();
              for (int i = 0; i < tot; ++i) {
                final OIdentifiable resultItem = readIdentifiable(network, iCommand.getDatabase());
                if (resultItem instanceof ORecord<?>)
                  iCommand.getDatabase().getLevel1Cache().updateRecord((ORecordInternal<?>) resultItem);
                list.add(resultItem);
              }
              result = list;
              break;

            case 'a':
              final String value = new String(network.readBytes());
              result = ORecordSerializerStringAbstract.fieldTypeFromStream(null, ORecordSerializerStringAbstract.getType(value),
                  value);
              break;
            }
          }
          break;
        } finally {
          endResponse(network);
        }

      } catch (OException e) {
        // PASS THROUGH
        throw e;
      } catch (Exception e) {
        handleException("Error on executing command: " + iCommand, e);

      } finally {
        OStorageRemoteThreadLocal.INSTANCE.get().commandExecuting = false;
      }
    } while (true);

    return result;
  }

  public void commit(final OTransaction iTx) {
    checkConnection();

    do {
      try {
        OChannelBinaryClient network = null;
        try {
          network = beginRequest(OChannelBinaryProtocol.REQUEST_TX_COMMIT);

          network.writeInt(((OTransaction) iTx).getId());
          network.writeByte((byte) (((OTransaction) iTx).isUsingLog() ? 1 : 0));

          final List<OTransactionRecordEntry> tmpEntries = new ArrayList<OTransactionRecordEntry>();

          while (iTx.getCurrentRecordEntries().iterator().hasNext()) {
            for (OTransactionRecordEntry txEntry : iTx.getCurrentRecordEntries())
              tmpEntries.add(txEntry);

            iTx.clearRecordEntries();

            if (tmpEntries.size() > 0)
              for (OTransactionRecordEntry txEntry : tmpEntries)
                commitEntry(network, txEntry);

          }

          // END OF RECORD ENTRIES
          network.writeByte((byte) 0);

          // SEND INDEX ENTRIES
          network.writeBytes(iTx.getIndexChanges().toStream());
        } finally {
          endRequest(network);
        }

        try {
          beginResponse(network);
          final int createdRecords = network.readInt();
          ORecordId currentRid;
          ORecordId createdRid;
          for (int i = 0; i < createdRecords; i++) {
            currentRid = network.readRID();
            createdRid = network.readRID();
            for (OTransactionRecordEntry txEntry : iTx.getAllRecordEntries()) {
              if (txEntry.getRecord().getIdentity().equals(currentRid)) {
                txEntry.getRecord().setIdentity(createdRid);
                break;
              }
            }
          }
          final int updatedRecords = network.readInt();
          ORecordId rid;
          for (int i = 0; i < updatedRecords; ++i) {
            rid = network.readRID();

            // SEARCH THE RECORD WITH THAT ID TO UPDATE THE VERSION
            for (OTransactionRecordEntry txEntry : iTx.getAllRecordEntries()) {
              if (txEntry.getRecord().getIdentity().equals(rid)) {
                txEntry.getRecord().setVersion(network.readInt());
                break;
              }
            }
          }
        } finally {
          endResponse(network);
        }

        // SET ALL THE RECORDS AS UNDIRTY
        for (OTransactionRecordEntry txEntry : iTx.getAllRecordEntries())
          txEntry.getRecord().unload();

        // UPDATE THE CACHE ONLY IF THE ITERATOR ALLOWS IT. USE THE STRATEGY TO ALWAYS REMOVE ALL THE RECORDS SINCE THEY COULD BE
        // CHANGED AS CONTENT IN CASE OF TREE AND GRAPH DUE TO CROSS REFERENCES
        OTransactionAbstract.updateCacheFromEntries(this, iTx, iTx.getAllRecordEntries(), false);

        break;
      } catch (OException e) {
        // PASS THROUGH
        throw e;
      } catch (Exception e) {
        handleException("Error on commit", e);

      }
    } while (true);
  }

  public void rollback(OTransaction iTx) {
  }

  public int getClusterIdByName(final String iClusterName) {
    checkConnection();

    if (iClusterName == null)
      return -1;

    if (Character.isDigit(iClusterName.charAt(0)))
      return Integer.parseInt(iClusterName);

    final Integer id = clustersIds.get(iClusterName.toLowerCase());
    if (id == null)
      return -1;

    return id;
  }

  public String getClusterTypeByName(final String iClusterName) {
    checkConnection();

    if (iClusterName == null)
      return null;

    return clustersTypes.get(iClusterName.toLowerCase());
  }

  public int getDefaultClusterId() {
    return defaultClusterId;
  }

  public int addCluster(final String iClusterName, final OStorage.CLUSTER_TYPE iClusterType, final Object... iArguments) {
    checkConnection();

    do {
      try {
        OChannelBinaryClient network = null;
        try {
          network = beginRequest(OChannelBinaryProtocol.REQUEST_DATACLUSTER_ADD);

          network.writeString(iClusterType.toString());
          network.writeString(iClusterName);

          switch (iClusterType) {
          case PHYSICAL:
            // FILE PATH + START SIZE
            network.writeString(iArguments.length > 0 ? (String) iArguments[0] : "").writeInt(
                iArguments.length > 0 ? (Integer) iArguments[1] : -1);
            break;

          case LOGICAL:
            // PHY CLUSTER ID
            network.writeInt(iArguments.length > 0 ? (Integer) iArguments[0] : -1);
            break;
          }
        } finally {
          endRequest(network);
        }

        try {
          beginResponse(network);
          final int clusterId = network.readShort();

          clustersIds.put(iClusterName.toLowerCase(), clusterId);
          clustersTypes.put(iClusterName.toLowerCase(), iClusterType.toString());
          return clusterId;
        } finally {
          endResponse(network);
        }
      } catch (OException e) {
        // PASS THROUGH
        throw e;
      } catch (Exception e) {
        handleException("Error on add new cluster", e);

      }
    } while (true);
  }

  public boolean dropCluster(final int iClusterId) {
    checkConnection();

    do {
      try {
        OChannelBinaryClient network = null;
        try {
          network = beginRequest(OChannelBinaryProtocol.REQUEST_DATACLUSTER_REMOVE);

          network.writeShort((short) iClusterId);

        } finally {
          endRequest(network);
        }

        try {
          beginResponse(network);

          if (network.readByte() == 1) {
            // REMOVE THE CLUSTER LOCALLY
            for (Entry<String, Integer> entry : clustersIds.entrySet())
              if (entry.getValue() != null && entry.getValue().intValue() == iClusterId) {
                clustersIds.remove(entry.getKey());
                clustersTypes.remove(entry.getKey());
                if (configuration.clusters.size() > iClusterId)
                  configuration.clusters.set(iClusterId, null);
                break;
              }
            getLevel2Cache().freeCluster(iClusterId);
            return true;
          }
          return false;
        } finally {
          endResponse(network);
        }

      } catch (OException e) {
        // PASS THROUGH
        throw e;
      } catch (Exception e) {
        handleException("Error on removing of cluster", e);

      }
    } while (true);
  }

  public int addDataSegment(final String iDataSegmentName) {
    return addDataSegment(iDataSegmentName, null);
  }

  public int addDataSegment(final String iSegmentName, final String iSegmentFileName) {
    checkConnection();

    do {
      try {
        OChannelBinaryClient network = null;
        try {
          network = beginRequest(OChannelBinaryProtocol.REQUEST_DATASEGMENT_ADD);

          network.writeString(iSegmentName).writeString(iSegmentFileName);

        } finally {
          endRequest(network);
        }

        try {
          beginResponse(network);
          return network.readShort();
        } finally {
          endResponse(network);
        }

      } catch (OException e) {
        // PASS THROUGH
        throw e;
      } catch (Exception e) {
        handleException("Error on add new data segment", e);

      }
    } while (true);
  }

  public void synch() {
  }

  public String getPhysicalClusterNameById(final int iClusterId) {
    for (Entry<String, Integer> clusterEntry : clustersIds.entrySet()) {
      if (clusterEntry.getValue().intValue() == iClusterId)
        return clusterEntry.getKey();
    }
    return null;
  }

  public int getClusters() {
    return clustersIds.size();
  }

  public Collection<OCluster> getClusterInstances() {
    throw new UnsupportedOperationException("getClusters()");
  }

  public OCluster getClusterById(final int iId) {
    throw new UnsupportedOperationException("getClusterById()");
  }

  @Override
  public long getVersion() {
    throw new UnsupportedOperationException("getVersion");
  }

  public ODocument getClusterConfiguration() {
    return clusterConfiguration;
  }

  /**
   * Handles exceptions. In case of IO errors retries to reconnect until the configured retry times has reached.
   *
   * @param iMessage
   * @param iException
   */
  protected void handleException(final String iMessage, final Exception iException) {
    if (iException instanceof OException)
      // RE-THROW IT
      throw (OException) iException;

    if (!(iException instanceof IOException))
      throw new OStorageException(iMessage, iException);

    if (status != STATUS.OPEN)
      // STORAGE CLOSED: DON'T HANDLE RECONNECTION
      return;

    final long lostConnectionTime = System.currentTimeMillis();

    final int currentMaxRetry;
    final int currentRetryDelay;
    if (clusterConfiguration != null) {
      // IN CLUSTER: NO RETRY AND 0 SLEEP TIME BETWEEN NODES
      currentMaxRetry = 1;
      currentRetryDelay = 0;
    } else {
      currentMaxRetry = connectionRetry;
      currentRetryDelay = connectionRetryDelay;
    }

    for (int retry = 0; retry < currentMaxRetry; ++retry) {
      // WAIT THE DELAY BEFORE TO RETRY
      if (currentRetryDelay > 0)
        try {
          Thread.sleep(currentRetryDelay);
        } catch (InterruptedException e) {
        }

      try {
        if (OLogManager.instance().isDebugEnabled())
          OLogManager.instance().debug(this, "Retrying to connect to remote server #" + retry + "/" + currentMaxRetry + "...");

        openRemoteDatabase();

        OLogManager.instance().info(this,
            "Connection re-acquired in transparent way after %dms and %d retries: no errors will be thrown at application level",
            System.currentTimeMillis() - lostConnectionTime, retry + 1);

        // RECONNECTED!
        return;

      } catch (Throwable t) {
        // DO NOTHING BUT CONTINUE IN THE LOOP
      }

      if (clusterConfiguration != null) {

      }
    }

    // RECONNECTION FAILED: THROW+LOG THE ORIGINAL EXCEPTION
    throw new OStorageException(iMessage, iException);
  }

  protected void openRemoteDatabase() throws IOException {
    minPool = OGlobalConfiguration.CLIENT_CHANNEL_MIN_POOL.getValueAsInteger();
    maxPool = OGlobalConfiguration.CLIENT_CHANNEL_MAX_POOL.getValueAsInteger();

    if (connectionOptions != null && connectionOptions.size() > 0) {
      if (connectionOptions.containsKey(PARAM_MIN_POOL))
        minPool = Integer.parseInt(connectionOptions.get(PARAM_MIN_POOL).toString());
      if (connectionOptions.containsKey(PARAM_MAX_POOL))
        maxPool = Integer.parseInt(connectionOptions.get(PARAM_MAX_POOL).toString());
    }

    setSessionId(-1);
    createConnectionPool();

    OChannelBinaryClient network = null;
    try {
      network = beginRequest(OChannelBinaryProtocol.REQUEST_DB_OPEN);

      network.writeString(name).writeString(connectionUserName).writeString(connectionUserPassword);

    } finally {
      endRequest(network);
    }

    final int sessionId;

    try {
      beginResponse(network);

      sessionId = network.readInt();

      OLogManager.instance().debug(null, "Client connected with session id: " + sessionId);

      readDatabaseInformation(network);

      // READ CLUSTER CONFIGURATION
      clusterConfiguration = new ODocument(network.readBytes());

    } finally {
      endResponse(network);
    }

    setSessionId(sessionId);

    defaultClusterId = clustersIds.get(OStorage.CLUSTER_DEFAULT_NAME);

    status = STATUS.OPEN;
  }

  /**
   * Parse the URL in the following formats:<br/>
   */
  protected void parseServerURLs() {
    String remoteHost;
    String[] remotePorts;

    int dbPos = url.indexOf('/');
    if (dbPos == -1) {
      // SHORT FORM
      name = url;
      remoteHost = getDefaultHost();
      remotePorts = getDefaultPort();
    } else {
      name = url.substring(dbPos + 1);

      int startPos = 0;
      int endPos = 0;

      while (endPos < dbPos) {
        if (url.indexOf(ADDRESS_SEPARATOR, startPos) > -1)
          endPos = url.indexOf(ADDRESS_SEPARATOR, startPos);
        else
          endPos = dbPos;

        int posRemotePort = url.indexOf(':', startPos);

        if (posRemotePort != -1 && posRemotePort < endPos) {
          remoteHost = url.substring(startPos, posRemotePort);
          remotePorts = url.substring(posRemotePort + 1, endPos).split("_");
          startPos = endPos + 1;
        } else {
          remoteHost = url.substring(startPos, endPos);
          remotePorts = getDefaultPort();
          startPos = endPos + 1;
        }

        // REGISTER THE REMOTE SERVER+PORT
        serverURLs.add(new OPair<String, String[]>(remoteHost, remotePorts));
      }
    }
  }

  protected String getDefaultHost() {
    return DEFAULT_HOST;
  }

  protected String[] getDefaultPort() {
    return DEFAULT_PORTS;
  }

  protected OChannelBinaryClient createNetworkConnection() throws IOException, UnknownHostException {
    int port;
    for (OPair<String, String[]> server : serverURLs) {
      port = Integer.parseInt(server.getValue()[server.getValue().length - 1]);

      OLogManager.instance().debug(this, "Trying to connect to the remote host %s:%d...", server.getKey(), port);
      final OChannelBinaryClient network = new OChannelBinaryClient(server.getKey(), port, clientConfiguration);

      OChannelBinaryProtocol.checkProtocolVersion(network);

      return network;
    }

    final StringBuilder buffer = new StringBuilder();
    for (OPair<String, String[]> server : serverURLs) {
      if (buffer.length() > 0)
        buffer.append(',');
      buffer.append(server.getKey());
    }

    throw new OIOException("Can't connect to any configured remote nodes: " + buffer);
  }

  protected void checkConnection() {
    lock.acquireSharedLock();

    try {
      synchronized (networkPool) {

        if (networkPool.size() == 0)
          throw new ODatabaseException("Connection is closed");
      }

    } finally {
      lock.releaseSharedLock();
    }
  }

  static OIdentifiable readIdentifiable(final OChannelBinaryClient network, final ODatabaseRecord iDatabase) throws IOException {
    final int classId = network.readShort();
    if (classId == OChannelBinaryProtocol.RECORD_NULL)
      return null;

    if (classId == OChannelBinaryProtocol.RECORD_RID) {
      return network.readRID();
    } else {
      final ORecordInternal<?> record = Orient.instance().getRecordFactoryManager().newInstance(iDatabase, network.readByte());

      if (record instanceof ORecordSchemaAware<?>)
        ((ORecordSchemaAware<?>) record).fill(iDatabase, classId, network.readRID(), network.readInt(), network.readBytes(), false);
      else
        // DISCARD CLASS ID
        record.fill(iDatabase, network.readRID(), network.readInt(), network.readBytes(), false);

      return record;
    }
  }

  /**
   * Acquire a network channel from the pool. Don't lock the write stream since the connection usage is exclusive.
   *
   * @param iCommand
   * @return
   * @throws IOException
   */
  protected OChannelBinaryClient beginRequest(final byte iCommand) throws IOException {
    OChannelBinaryClient network = null;

    if (debug)
      System.out.println("-> req: " + getSessionId());

    // FIND THE FIRST FREE CHANNEL AVAILABLE
    synchronized (networkPool) {
      final int beginCursor = networkPoolCursor;

      while (network == null) {
        if (networkPool.size() == 0)
          throw new ONetworkProtocolException("Connection pool closed");

        network = networkPool.get(networkPoolCursor);
        if (network.getLockWrite().tryLock())
          break;

        network = null;

        networkPoolCursor++;

        if (networkPoolCursor >= networkPool.size())
          // RESTART FROM THE FIRST ONE
          networkPoolCursor = 0;

        if (networkPoolCursor == beginCursor) {
          // COMPLETE ROUND AND NOT FREE CONNECTIONS FOUND

          if (networkPool.size() < maxPool) {
            // CREATE NEW CONNECTION
            network = createNetworkConnection();
            network.getLockWrite().lock();
            networkPool.add(network);

            if (debug)
              System.out.println("Created new connection " + networkPool.size());
          } else {
            if (debug)
              System.out.println("-> req (waiting) : " + getSessionId());

            final long startToWait = System.currentTimeMillis();
            try {
              networkPool.wait(5000);
            } catch (InterruptedException e) {
              OProfiler.getInstance().updateCounter("network.connectionPool.timeout", +1);
            }

            final long elapsed = OProfiler.getInstance().stopChrono("network.connectionPool.waitingTime", startToWait);

            if (debug)
              System.out.println("Waiting for connection = elapsed: " + elapsed);
          }
        }
      }
    }

    network.writeByte(iCommand);
    network.writeInt(getSessionId());

    return network;
  }

  /**
   * Ends the request and unlock the write lock
   */
  public void endRequest(final OChannelBinaryClient iNetwork) throws IOException {
    if (iNetwork == null)
      return;

    try {
      iNetwork.flush();
      // } catch (IOException e) {
      // IGNORE IT BECAUSE IT COULD BE CALLED AFTER A NETWORK ERROR TO RELEASE THE SOCKET
    } finally {

      iNetwork.getLockWrite().unlock();

      if (debug)
        System.out.println("<- req: " + getSessionId());

      synchronized (networkPool) {
        networkPool.notifyAll();
      }
    }
  }

  /**
   * Closes the channel and remove it from the pool.
   *
   * @param iNetwork
   *          Channel to close and remove
   */
  protected void closeChannel(final OChannelBinaryClient iNetwork) {
    iNetwork.close();
    synchronized (networkPool) {
      networkPool.remove(iNetwork);
    }
  }

  /**
   * Starts listening the response.
   */
  protected void beginResponse(final OChannelBinaryClient iNetwork) throws IOException {
    iNetwork.beginResponse(getSessionId());

    if (debug)
      System.out.println("-> res: " + getSessionId());
  }

  /**
   * End response reached: release the channel in the pool to being reused
   */
  public void endResponse(final OChannelBinaryClient iNetwork) {
    iNetwork.endResponse();

    if (debug)
      System.out.println("<- res: " + getSessionId());
  }

  public boolean isPermanentRequester() {
    return false;
  }

  protected void getResponse(final OChannelBinaryClient iNetwork) throws IOException {
    try {
      beginResponse(iNetwork);
    } finally {
      endResponse(iNetwork);
    }
  }

  public void updateClusterConfiguration(final byte[] iContent) {
    if (iContent == null)
      return;

    synchronized (clusterConfiguration) {
      clusterConfiguration.reset();

      // UPDATE IT
      clusterConfiguration.fromStream(iContent);

      if (OLogManager.instance().isDebugEnabled())
        OLogManager.instance().debug(this, "Received new cluster configuration: %s", clusterConfiguration.toJSON(""));
    }
  }

  private void commitEntry(final OChannelBinaryClient iNetwork, final OTransactionRecordEntry txEntry) throws IOException {
    if (txEntry.status == OTransactionRecordEntry.LOADED)
      // JUMP LOADED OBJECTS
      return;

    iNetwork.writeByte((byte) 1);
    iNetwork.writeByte(txEntry.status);
    iNetwork.writeShort((short) txEntry.getRecord().getIdentity().getClusterId());
    iNetwork.writeLong(txEntry.getRecord().getIdentity().getClusterPosition());
    iNetwork.writeByte(txEntry.getRecord().getRecordType());

    switch (txEntry.status) {
    case OTransactionRecordEntry.CREATED:
      iNetwork.writeString(txEntry.clusterName);
      iNetwork.writeBytes(txEntry.getRecord().toStream());
      break;

    case OTransactionRecordEntry.UPDATED:
      iNetwork.writeInt(txEntry.getRecord().getVersion());
      iNetwork.writeBytes(txEntry.getRecord().toStream());
      break;

    case OTransactionRecordEntry.DELETED:
      iNetwork.writeInt(txEntry.getRecord().getVersion());
      break;
    }
  }

  protected void createConnectionPool() throws IOException, UnknownHostException {
    synchronized (networkPool) {
      if (!networkPool.isEmpty()) {
        // CHECK EXISTENT NETWORK CONNECTIONS
        final List<OChannelBinaryClient> editableList = new ArrayList<OChannelBinaryClient>(networkPool);
        for (OChannelBinaryClient net : editableList) {
          if (!net.isConnected())
            // CLOSE IT AND REMOVE FROM THE LIST
            closeChannel(net);
        }
      }

      // CREATE THE CHANNEL POOL
      if (networkPool.size() == 0) {
        // ALWAYS CREATE AT LEAST ONE CONNECTION
        final OChannelBinaryClient firstChannel = createNetworkConnection();
        networkPool.add(firstChannel);
        serviceThread = new OStorageRemoteServiceThread(new OStorageRemoteThread(this, Integer.MIN_VALUE), firstChannel);

        // CREATE THE MINIMUM POOL
        for (int i = 1; i < minPool; ++i)
          networkPool.add(createNetworkConnection());
      }

    }
  }

  private void readDatabaseInformation(final OChannelBinaryClient network) throws IOException {
    int tot = network.readInt();
    String clusterName;
    clustersIds.clear();
    clustersTypes.clear();
    for (int i = 0; i < tot; ++i) {
      clusterName = network.readString().toLowerCase();
      clustersIds.put(clusterName, network.readInt());
      clustersTypes.put(clusterName, network.readString());
    }

    defaultClusterId = clustersIds.get(OStorage.CLUSTER_DEFAULT_NAME);
  }
}
TOP

Related Classes of com.orientechnologies.orient.client.remote.OStorageRemote

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.