Package com.orientechnologies.orient.server.network.protocol.binary

Source Code of com.orientechnologies.orient.server.network.protocol.binary.ONetworkProtocolBinary

/*
    *
    *  *  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.server.network.protocol.binary;

import java.io.IOException;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.net.SocketException;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.UUID;

import com.orientechnologies.common.collection.OMultiValue;
import com.orientechnologies.common.concur.lock.OLockException;
import com.orientechnologies.common.io.OIOException;
import com.orientechnologies.common.log.OLogManager;
import com.orientechnologies.common.serialization.types.OBinarySerializer;
import com.orientechnologies.common.serialization.types.OByteSerializer;
import com.orientechnologies.common.serialization.types.OIntegerSerializer;
import com.orientechnologies.common.serialization.types.ONullSerializer;
import com.orientechnologies.orient.client.remote.OCollectionNetworkSerializer;
import com.orientechnologies.orient.client.remote.OEngineRemote;
import com.orientechnologies.orient.core.OConstants;
import com.orientechnologies.orient.core.Orient;
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.db.ODatabase;
import com.orientechnologies.orient.core.db.ODatabaseComplex;
import com.orientechnologies.orient.core.db.ODatabaseComplexInternal;
import com.orientechnologies.orient.core.db.ODatabaseRecordThreadLocal;
import com.orientechnologies.orient.core.db.document.ODatabaseDocument;
import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx;
import com.orientechnologies.orient.core.db.record.ODatabaseRecordTx;
import com.orientechnologies.orient.core.db.record.OIdentifiable;
import com.orientechnologies.orient.core.db.record.ridbag.sbtree.OBonsaiCollectionPointer;
import com.orientechnologies.orient.core.db.record.ridbag.sbtree.OSBTreeCollectionManager;
import com.orientechnologies.orient.core.db.record.ridbag.sbtree.OSBTreeRidBag;
import com.orientechnologies.orient.core.exception.OConfigurationException;
import com.orientechnologies.orient.core.exception.ODatabaseException;
import com.orientechnologies.orient.core.exception.OSecurityAccessException;
import com.orientechnologies.orient.core.exception.OSecurityException;
import com.orientechnologies.orient.core.exception.OStorageException;
import com.orientechnologies.orient.core.exception.OTransactionAbortedException;
import com.orientechnologies.orient.core.fetch.OFetchContext;
import com.orientechnologies.orient.core.fetch.OFetchHelper;
import com.orientechnologies.orient.core.fetch.OFetchListener;
import com.orientechnologies.orient.core.fetch.remote.ORemoteFetchContext;
import com.orientechnologies.orient.core.fetch.remote.ORemoteFetchListener;
import com.orientechnologies.orient.core.id.OClusterPosition;
import com.orientechnologies.orient.core.id.ORID;
import com.orientechnologies.orient.core.id.ORecordId;
import com.orientechnologies.orient.core.index.sbtree.OTreeInternal;
import com.orientechnologies.orient.core.index.sbtreebonsai.local.OSBTreeBonsai;
import com.orientechnologies.orient.core.metadata.schema.OType;
import com.orientechnologies.orient.core.metadata.security.OUser;
import com.orientechnologies.orient.core.record.ORecord;
import com.orientechnologies.orient.core.record.ORecordInternal;
import com.orientechnologies.orient.core.record.impl.ODocument;
import com.orientechnologies.orient.core.record.impl.ORecordBytes;
import com.orientechnologies.orient.core.serialization.OMemoryStream;
import com.orientechnologies.orient.core.serialization.serializer.ONetworkThreadLocalSerializer;
import com.orientechnologies.orient.core.serialization.serializer.record.ORecordSerializer;
import com.orientechnologies.orient.core.serialization.serializer.record.ORecordSerializerFactory;
import com.orientechnologies.orient.core.serialization.serializer.record.string.ORecordSerializerSchemaAware2CSV;
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.OPhysicalPosition;
import com.orientechnologies.orient.core.storage.ORecordMetadata;
import com.orientechnologies.orient.core.storage.OStorage;
import com.orientechnologies.orient.core.storage.OStorageProxy;
import com.orientechnologies.orient.core.storage.impl.memory.ODirectMemoryStorage;
import com.orientechnologies.orient.core.type.ODocumentWrapper;
import com.orientechnologies.orient.core.version.ORecordVersion;
import com.orientechnologies.orient.core.version.OVersionFactory;
import com.orientechnologies.orient.enterprise.channel.binary.OChannelBinaryProtocol;
import com.orientechnologies.orient.enterprise.channel.binary.OChannelBinaryServer;
import com.orientechnologies.orient.server.OClientConnection;
import com.orientechnologies.orient.server.OClientConnectionManager;
import com.orientechnologies.orient.server.OServer;
import com.orientechnologies.orient.server.ShutdownHelper;
import com.orientechnologies.orient.server.distributed.ODistributedServerManager;
import com.orientechnologies.orient.server.network.OServerNetworkListener;
import com.orientechnologies.orient.server.plugin.OServerPlugin;
import com.orientechnologies.orient.server.plugin.OServerPluginHelper;
import com.orientechnologies.orient.server.tx.OTransactionOptimisticProxy;

public class ONetworkProtocolBinary extends OBinaryNetworkProtocolAbstract {
   protected OClientConnection connection;
   protected OUser             account;

   public ONetworkProtocolBinary() {
     super("OrientDB <- BinaryClient/?");
   }

   public ONetworkProtocolBinary(final String iThreadName) {
     super(iThreadName);
   }

   @Override
   public void config(final OServerNetworkListener iListener, final OServer iServer, final Socket iSocket,
       final OContextConfiguration iConfig) throws IOException {
     // CREATE THE CLIENT CONNECTION
     connection = OClientConnectionManager.instance().connect(this);

     super.config(iListener, iServer, iSocket, iConfig);

     // SEND PROTOCOL VERSION
     channel.writeShort((short) getVersion());

     channel.flush();
     start();

     setName("OrientDB <- BinaryClient (" + iSocket.getRemoteSocketAddress() + ")");
   }

   @Override
   public void startup() {
     super.startup();
     OServerPluginHelper.invokeHandlerCallbackOnClientConnection(server, connection);
   }

   @Override
   public void shutdown() {
     sendShutdown();
     super.shutdown();

     if (connection == null)
       return;

     OServerPluginHelper.invokeHandlerCallbackOnClientDisconnection(server, connection);

     OClientConnectionManager.instance().disconnect(connection);
   }

   @Override
   protected void onBeforeRequest() throws IOException {
     waitNodeIsOnline();

     connection = OClientConnectionManager.instance().getConnection(clientTxId, this);

     if (clientTxId < 0) {
       short protocolId = 0;

       if (connection != null)
         protocolId = connection.data.protocolVersion;

       connection = OClientConnectionManager.instance().connect(this);

       if (connection != null)
         connection.data.protocolVersion = protocolId;
     }

     if (connection != null) {
       ODatabaseRecordThreadLocal.INSTANCE.set(connection.database);
       if (connection.database != null) {
         connection.data.lastDatabase = connection.database.getName();
         connection.data.lastUser = connection.database.getUser() != null ? connection.database.getUser().getName() : null;
       } else {
         connection.data.lastDatabase = null;
         connection.data.lastUser = null;
       }

       ++connection.data.totalRequests;
       setDataCommandInfo("Listening");
       connection.data.commandDetail = "-";
       connection.data.lastCommandReceived = System.currentTimeMillis();
     } else {
       ODatabaseRecordThreadLocal.INSTANCE.remove();
       if (requestType != OChannelBinaryProtocol.REQUEST_DB_CLOSE && requestType != OChannelBinaryProtocol.REQUEST_SHUTDOWN) {
         OLogManager.instance().debug(this, "Found unknown session %d, shutdown current connection", clientTxId);
         shutdown();
         throw new OIOException("Found unknown session " + clientTxId);
       }
     }

     OServerPluginHelper.invokeHandlerCallbackOnBeforeClientRequest(server, connection, (byte) requestType);
   }

   @Override
   protected void onAfterRequest() throws IOException {
     OServerPluginHelper.invokeHandlerCallbackOnAfterClientRequest(server, connection, (byte) requestType);

     if (connection != null) {
       if (connection.database != null)
         if (!connection.database.isClosed())
           connection.database.getLocalCache().clear();

       connection.data.lastCommandExecutionTime = System.currentTimeMillis() - connection.data.lastCommandReceived;
       connection.data.totalCommandExecutionTime += connection.data.lastCommandExecutionTime;

       connection.data.lastCommandInfo = connection.data.commandInfo;
       connection.data.lastCommandDetail = connection.data.commandDetail;

       setDataCommandInfo("Listening");
       connection.data.commandDetail = "-";
     }
   }

   protected boolean executeRequest() throws IOException {
     try {
       switch (requestType) {

       case OChannelBinaryProtocol.REQUEST_SHUTDOWN:
         shutdownConnection();
         break;

       case OChannelBinaryProtocol.REQUEST_CONNECT:
         connect();
         break;

       case OChannelBinaryProtocol.REQUEST_DB_LIST:
         listDatabases();
         break;

       case OChannelBinaryProtocol.REQUEST_DB_OPEN:
         openDatabase();
         break;

       case OChannelBinaryProtocol.REQUEST_DB_RELOAD:
         reloadDatabase();
         break;

       case OChannelBinaryProtocol.REQUEST_DB_CREATE:
         createDatabase();
         break;

       case OChannelBinaryProtocol.REQUEST_DB_CLOSE:
         closeDatabase();
         break;

       case OChannelBinaryProtocol.REQUEST_DB_EXIST:
         existsDatabase();
         break;

       case OChannelBinaryProtocol.REQUEST_DB_DROP:
         dropDatabase();
         break;

       case OChannelBinaryProtocol.REQUEST_DB_SIZE:
         sizeDatabase();
         break;

       case OChannelBinaryProtocol.REQUEST_DB_COUNTRECORDS:
         countDatabaseRecords();
         break;

       case OChannelBinaryProtocol.REQUEST_DB_COPY:
         copyDatabase();
         break;

       case OChannelBinaryProtocol.REQUEST_REPLICATION:
         replicationDatabase();
         break;

       case OChannelBinaryProtocol.REQUEST_CLUSTER:
         distributedCluster();
         break;

       case OChannelBinaryProtocol.REQUEST_DATACLUSTER_COUNT:
         countClusters();
         break;

       case OChannelBinaryProtocol.REQUEST_DATACLUSTER_DATARANGE:
         rangeCluster();
         break;

       case OChannelBinaryProtocol.REQUEST_DATACLUSTER_ADD:
         addCluster();
         break;

       case OChannelBinaryProtocol.REQUEST_DATACLUSTER_DROP:
         removeCluster();
         break;

       case OChannelBinaryProtocol.REQUEST_RECORD_METADATA:
         readRecordMetadata();
         break;

       case OChannelBinaryProtocol.REQUEST_RECORD_LOAD:
         readRecord();
         break;

       case OChannelBinaryProtocol.REQUEST_RECORD_CREATE:
         createRecord();
         break;

       case OChannelBinaryProtocol.REQUEST_RECORD_UPDATE:
         updateRecord();
         break;

       case OChannelBinaryProtocol.REQUEST_RECORD_DELETE:
         deleteRecord();
         break;

       case OChannelBinaryProtocol.REQUEST_RECORD_HIDE:
         hideRecord();
         break;

       case OChannelBinaryProtocol.REQUEST_POSITIONS_HIGHER:
         higherPositions();
         break;

       case OChannelBinaryProtocol.REQUEST_POSITIONS_CEILING:
         ceilingPositions();
         break;

       case OChannelBinaryProtocol.REQUEST_POSITIONS_LOWER:
         lowerPositions();
         break;

       case OChannelBinaryProtocol.REQUEST_POSITIONS_FLOOR:
         floorPositions();
         break;

       case OChannelBinaryProtocol.REQUEST_COUNT:
         throw new UnsupportedOperationException("Operation OChannelBinaryProtocol.REQUEST_COUNT has been deprecated");

       case OChannelBinaryProtocol.REQUEST_COMMAND:
         command();
         break;

       case OChannelBinaryProtocol.REQUEST_TX_COMMIT:
         commit();
         break;

       case OChannelBinaryProtocol.REQUEST_CONFIG_GET:
         configGet();
         break;

       case OChannelBinaryProtocol.REQUEST_CONFIG_SET:
         configSet();
         break;

       case OChannelBinaryProtocol.REQUEST_CONFIG_LIST:
         configList();
         break;

       case OChannelBinaryProtocol.REQUEST_DB_FREEZE:
         freezeDatabase();
         break;

       case OChannelBinaryProtocol.REQUEST_DB_RELEASE:
         releaseDatabase();
         break;

       case OChannelBinaryProtocol.REQUEST_DATACLUSTER_FREEZE:
         freezeCluster();
         break;

       case OChannelBinaryProtocol.REQUEST_DATACLUSTER_RELEASE:
         releaseCluster();
         break;

       case OChannelBinaryProtocol.REQUEST_RECORD_CLEAN_OUT:
         cleanOutRecord();
         break;

       case OChannelBinaryProtocol.REQUEST_CREATE_SBTREE_BONSAI:
         createSBTreeBonsai();
         break;

       case OChannelBinaryProtocol.REQUEST_SBTREE_BONSAI_GET:
         sbTreeBonsaiGet();
         break;

       case OChannelBinaryProtocol.REQUEST_SBTREE_BONSAI_FIRST_KEY:
         sbTreeBonsaiFirstKey();
         break;

       case OChannelBinaryProtocol.REQUEST_SBTREE_BONSAI_GET_ENTRIES_MAJOR:
         sbTreeBonsaiGetEntriesMajor();
         break;

       case OChannelBinaryProtocol.REQUEST_RIDBAG_GET_SIZE:
         ridBagSize();
         break;

       default:
         setDataCommandInfo("Command not supported");
         return false;
       }

       return true;
     } catch (RuntimeException e) {
       if (connection != null && connection.database != null) {
         final OSBTreeCollectionManager collectionManager = connection.database.getSbTreeCollectionManager();
         if (collectionManager != null)
           collectionManager.clearChangedIds();
       }

       throw e;
     }
   }

   protected void checkServerAccess(final String iResource) {
     if (connection.serverUser == null)
       throw new OSecurityAccessException("Server user not authenticated.");

     if (!server.authenticate(connection.serverUser.name, null, iResource))
       throw new OSecurityAccessException("User '" + connection.serverUser.name + "' cannot access to the resource [" + iResource
           + "]. Use another server user or change permission in the file config/orientdb-server-config.xml");
   }

   protected ODatabaseComplex<?> openDatabase(final ODatabaseComplexInternal<?> database, final String iUser, final String iPassword) {

     if (database.isClosed())
       if (database.getStorage() instanceof ODirectMemoryStorage && !database.exists())
         database.create();
       else {
         try {
           database.open(iUser, iPassword);
         } catch (OSecurityException e) {
           // TRY WITH SERVER'S USER
           try {
             connection.serverUser = server.serverLogin(iUser, iPassword, "database.passthrough");
           } catch (OSecurityException ex) {
             throw e;
           }

           // SERVER AUTHENTICATED, BYPASS SECURITY
           database.setProperty(ODatabase.OPTIONS.SECURITY.toString(), Boolean.FALSE);
           database.open(iUser, iPassword);
         }
       }

     return database;
   }

   protected void removeCluster() throws IOException {
     setDataCommandInfo("Remove cluster");

     if (!isConnectionAlive())
       return;

     final int id = channel.readShort();

     final String clusterName = connection.database.getClusterNameById(id);
     if (clusterName == null)
       throw new IllegalArgumentException("Cluster " + id
           + " doesn't exist anymore. Refresh the db structure or just reconnect to the database");

     boolean result = connection.database.dropCluster(clusterName, true);

     beginResponse();
     try {
       sendOk(clientTxId);
       channel.writeByte((byte) (result ? 1 : 0));
     } finally {
       endResponse();
     }
   }

   protected void addCluster() throws IOException {
     setDataCommandInfo("Add cluster");

     if (!isConnectionAlive())
       return;

     String type = "";
     if (connection.data.protocolVersion < 24)
       type = channel.readString();

     final String name = channel.readString();
     int clusterId = -1;

     final String location;
     if (connection.data.protocolVersion >= 10 && connection.data.protocolVersion < 24 || type.equalsIgnoreCase("PHYSICAL"))
       location = channel.readString();
     else
       location = null;

     if (connection.data.protocolVersion < 24) {
       final String dataSegmentName;
       if (connection.data.protocolVersion >= 10)
         dataSegmentName = channel.readString();
       else {
         channel.readInt(); // OLD INIT SIZE, NOT MORE USED
         dataSegmentName = null;
       }
     }

     if (connection.data.protocolVersion >= 18)
       clusterId = channel.readShort();

     final int num;
     if (clusterId < 0)
       num = connection.database.addCluster(name);
     else
       num = connection.database.addCluster(name, clusterId, null);

     beginResponse();
     try {
       sendOk(clientTxId);
       channel.writeShort((short) num);
     } finally {
       endResponse();
     }
   }

   protected void rangeCluster() throws IOException {
     setDataCommandInfo("Get the begin/end range of data in cluster");

     if (!isConnectionAlive())
       return;

     OClusterPosition[] pos = connection.database.getStorage().getClusterDataRange(channel.readShort());

     beginResponse();
     try {
       sendOk(clientTxId);
       channel.writeClusterPosition(pos[0]);
       channel.writeClusterPosition(pos[1]);
     } finally {
       endResponse();
     }
   }

   protected void countClusters() throws IOException {
     setDataCommandInfo("Count cluster elements");

     if (!isConnectionAlive())
       return;

     int[] clusterIds = new int[channel.readShort()];
     for (int i = 0; i < clusterIds.length; ++i)
       clusterIds[i] = channel.readShort();

     boolean countTombstones = false;
     if (connection.data.protocolVersion >= 13)
       countTombstones = channel.readByte() > 0;

     final long count = connection.database.countClusterElements(clusterIds, countTombstones);

     beginResponse();
     try {
       sendOk(clientTxId);
       channel.writeLong(count);
     } finally {
       endResponse();
     }
   }

   protected void reloadDatabase() throws IOException {
     setDataCommandInfo("Reload database information");

     if (!isConnectionAlive())
       return;

     beginResponse();
     try {
       sendOk(clientTxId);

       sendDatabaseInformation();

     } finally {
       endResponse();
     }
   }

   protected void openDatabase() throws IOException {
     setDataCommandInfo("Open database");

     readConnectionData();

     final String dbURL = channel.readString();

     String dbType = ODatabaseDocument.TYPE;
     if (connection.data.protocolVersion >= 8)
       // READ DB-TYPE FROM THE CLIENT
       dbType = channel.readString();

     final String user = channel.readString();
     final String passwd = channel.readString();

     connection.database = (ODatabaseDocumentTx) server.openDatabase(dbType, dbURL, user, passwd);
     connection.rawDatabase = ((ODatabaseComplexInternal<?>) connection.database.getUnderlying()).getUnderlying();

     if (connection.database.getStorage() instanceof OStorageProxy && !loadUserFromSchema(user, passwd)) {
       sendErrorOrDropConnection(clientTxId, new OSecurityAccessException(connection.database.getName(),
           "User or password not valid for database: '" + connection.database.getName() + "'"));
     } else {

       beginResponse();
       try {
         sendOk(clientTxId);
         channel.writeInt(connection.id);

         sendDatabaseInformation();

         final OServerPlugin plugin = server.getPlugin("cluster");
         ODocument distributedCfg = null;
         if (plugin != null && plugin instanceof ODistributedServerManager)
           distributedCfg = ((ODistributedServerManager) plugin).getClusterConfiguration();

         channel.writeBytes(distributedCfg != null ? distributedCfg.toStream() : null);

         if (connection.data.protocolVersion >= 14)
           channel.writeString(OConstants.getVersion());
       } finally {
         endResponse();
       }
     }
   }

   protected void connect() throws IOException {
     setDataCommandInfo("Connect");

     readConnectionData();

     connection.serverUser = server.serverLogin(channel.readString(), channel.readString(), "connect");

     beginResponse();
     try {
       sendOk(clientTxId);
       channel.writeInt(connection.id);
     } finally {
       endResponse();
     }
   }

   protected void sendError(final int iClientTxId, final Throwable t) throws IOException {
     channel.acquireWriteLock();
     try {

       channel.writeByte(OChannelBinaryProtocol.RESPONSE_STATUS_ERROR);
       channel.writeInt(iClientTxId);

       final Throwable current;
       if (t instanceof OLockException && t.getCause() instanceof ODatabaseException)
         // BYPASS THE DB POOL EXCEPTION TO PROPAGATE THE RIGHT SECURITY ONE
         current = t.getCause();
       else
         current = t;

       sendErrorDetails(current);

       if (connection != null && connection.data.protocolVersion >= 19) {
         serializeExceptionObject(current);
       }

       channel.flush();

       if (OLogManager.instance().isLevelEnabled(logClientExceptions)) {
         if (logClientFullStackTrace)
           OLogManager.instance().log(this, logClientExceptions, "Sent run-time exception to the client %s: %s", t,
               channel.socket.getRemoteSocketAddress(), t.toString());
         else
           OLogManager.instance().log(this, logClientExceptions, "Sent run-time exception to the client %s: %s", null,
               channel.socket.getRemoteSocketAddress(), t.toString());
       }
     } catch (Exception e) {
       if (e instanceof SocketException)
         shutdown();
       else
         OLogManager.instance().error(this, "Error during sending an error to client", e);
     } finally {
       if (channel.getLockWrite().isHeldByCurrentThread())
         // NO EXCEPTION SO FAR: UNLOCK IT
         channel.releaseWriteLock();
     }
   }

   protected void shutdownConnection() throws IOException {
     setDataCommandInfo("Shutdowning");

     OLogManager.instance().info(this, "Received shutdown command from the remote client %s:%d", channel.socket.getInetAddress(),
         channel.socket.getPort());

     final String user = channel.readString();
     final String passwd = channel.readString();

     if (server.authenticate(user, passwd, "shutdown")) {
       OLogManager.instance().info(this, "Remote client %s:%d authenticated. Starting shutdown of server...",
           channel.socket.getInetAddress(), channel.socket.getPort());

       beginResponse();
       try {
         sendOk(clientTxId);
       } finally {
         endResponse();
       }
       runShutdownInNonDaemonThread();
     }

     OLogManager.instance().error(this, "Authentication error of remote client %s:%d: shutdown is aborted.",
         channel.socket.getInetAddress(), channel.socket.getPort());

     sendErrorOrDropConnection(clientTxId, new OSecurityAccessException("Invalid user/password to shutdown the server"));
   }

   protected void copyDatabase() throws IOException {
     setDataCommandInfo("Copy the database to a remote server");

     final String dbUrl = channel.readString();
     final String dbUser = channel.readString();
     final String dbPassword = channel.readString();
     final String remoteServerName = channel.readString();
     final String remoteServerEngine = channel.readString();

     checkServerAccess("database.copy");

     final ODatabaseDocumentTx db = (ODatabaseDocumentTx) server.openDatabase(ODatabaseDocument.TYPE, dbUrl, dbUser, dbPassword);

     beginResponse();
     try {
       sendOk(clientTxId);
     } finally {
       endResponse();
     }
   }

   protected void replicationDatabase() throws IOException {
     setDataCommandInfo("Replication command");

     final ODocument request = new ODocument(channel.readBytes());

     final ODistributedServerManager dManager = server.getDistributedManager();
     if (dManager == null)
       throw new OConfigurationException("No distributed manager configured");

     final String operation = request.field("operation");

     ODocument response = null;

     if (operation.equals("start")) {
       checkServerAccess("server.replication.start");

     } else if (operation.equals("stop")) {
       checkServerAccess("server.replication.stop");

     } else if (operation.equals("config")) {
       checkServerAccess("server.replication.config");

       response = new ODocument().fromJSON(dManager.getDatabaseConfiguration((String) request.field("db")).serialize()
           .toJSON("prettyPrint"));

     }

     sendResponse(response);

   }

   protected void distributedCluster() throws IOException {
     setDataCommandInfo("Cluster status");

     final ODocument req = new ODocument(channel.readBytes());

     ODocument response = null;

     final String operation = req.field("operation");
     if (operation == null)
       throw new IllegalArgumentException("Cluster operation is null");

     if (operation.equals("status")) {
       final OServerPlugin plugin = server.getPlugin("cluster");
       if (plugin != null && plugin instanceof ODistributedServerManager)
         response = ((ODistributedServerManager) plugin).getClusterConfiguration();
     } else
       throw new IllegalArgumentException("Cluster operation '" + operation + "' is not supported");

     sendResponse(response);
   }

   protected void countDatabaseRecords() throws IOException {
     setDataCommandInfo("Database count records");

     if (!isConnectionAlive())
       return;

     beginResponse();
     try {
       sendOk(clientTxId);
       channel.writeLong(connection.database.getStorage().countRecords());
     } finally {
       endResponse();
     }
   }

   protected void sizeDatabase() throws IOException {
     setDataCommandInfo("Database size");

     if (!isConnectionAlive())
       return;

     beginResponse();
     try {
       sendOk(clientTxId);
       channel.writeLong(connection.database.getStorage().getSize());
     } finally {
       endResponse();
     }
   }

   protected void dropDatabase() throws IOException {
     setDataCommandInfo("Drop database");
     String dbName = channel.readString();

     String storageType;
     if (connection.data.protocolVersion >= 16)
       storageType = channel.readString();
     else
       storageType = "local";

     checkServerAccess("database.delete");

     connection.database = getDatabaseInstance(dbName, ODatabaseDocument.TYPE, storageType);

     if (connection.database.exists()) {
       OLogManager.instance().info(this, "Dropped database '%s'", connection.database.getName());

       if (connection.database.isClosed())
         openDatabase(connection.database, connection.serverUser.name, connection.serverUser.password);

       connection.database.drop();
       connection.close();
     } else {
       throw new OStorageException("Database with name '" + dbName + "' doesn't exits.");
     }

     beginResponse();
     try {
       sendOk(clientTxId);
     } finally {
       endResponse();
     }
   }

   protected void existsDatabase() throws IOException {
     setDataCommandInfo("Exists database");
     final String dbName = channel.readString();
     final String storageType;

     if (connection.data.protocolVersion >= 16)
       storageType = channel.readString();
     else
       storageType = "local";

     checkServerAccess("database.exists");

     if (storageType != null)
       connection.database = getDatabaseInstance(dbName, ODatabaseDocument.TYPE, storageType);
     else {
       // CHECK AGAINST ALL THE ENGINE TYPES, BUT REMOTE
       for (String engine : Orient.instance().getEngines()) {
         if (!engine.equalsIgnoreCase(OEngineRemote.NAME)) {
           connection.database = getDatabaseInstance(dbName, ODatabaseDocument.TYPE, engine);
           if (connection.database.exists())
             // FOUND
             break;

           // NOT FOUND: ASSURE TO UNREGISTER IT TO AVOID CACHING
           Orient.instance().unregisterStorage(connection.database.getStorage());
         }
       }
     }

     beginResponse();
     try {
       sendOk(clientTxId);
       channel.writeByte((byte) (connection.database.exists() ? 1 : 0));
     } finally {
       endResponse();
     }
   }

   protected void createDatabase() throws IOException {
     setDataCommandInfo("Create database");

     String dbName = channel.readString();
     String dbType = ODatabaseDocument.TYPE;
     if (connection.data.protocolVersion >= 8)
       // READ DB-TYPE FROM THE CLIENT
       dbType = channel.readString();
     String storageType = channel.readString();

     checkServerAccess("database.create");
     checkStorageExistence(dbName);
     connection.database = getDatabaseInstance(dbName, dbType, storageType);
     createDatabase(connection.database, null, null);
     connection.rawDatabase = (((ODatabaseComplexInternal<?>) connection.database.getUnderlying()).getUnderlying());

     beginResponse();
     try {
       sendOk(clientTxId);
     } finally {
       endResponse();
     }
   }

   protected void closeDatabase() throws IOException {
     setDataCommandInfo("Close Database");

     if (connection != null) {
       if (connection.data.protocolVersion > 0 && connection.data.protocolVersion < 9)
         // OLD CLIENTS WAIT FOR A OK
         sendOk(clientTxId);

       if (OClientConnectionManager.instance().disconnect(connection.id))
         sendShutdown();
     }
   }

   protected void configList() throws IOException {
     setDataCommandInfo("List config");

     checkServerAccess("server.config.get");

     beginResponse();
     try {
       sendOk(clientTxId);

       channel.writeShort((short) OGlobalConfiguration.values().length);
       for (OGlobalConfiguration cfg : OGlobalConfiguration.values()) {

         String key;
         try {
           key = cfg.getKey();
         } catch (Exception e) {
           key = "?";
         }

         String value;
         try {
           value = cfg.getValueAsString() != null ? cfg.getValueAsString() : "";
         } catch (Exception e) {
           value = "";
         }

         channel.writeString(key);
         channel.writeString(value);
       }
     } finally {
       endResponse();
     }
   }

   protected void configSet() throws IOException {
     setDataCommandInfo("Get config");

     checkServerAccess("server.config.set");

     final String key = channel.readString();
     final String value = channel.readString();
     final OGlobalConfiguration cfg = OGlobalConfiguration.findByKey(key);
     if (cfg != null)
       cfg.setValue(value);

     beginResponse();
     try {
       sendOk(clientTxId);
     } finally {
       endResponse();
     }
   }

   protected void configGet() throws IOException {
     setDataCommandInfo("Get config");

     checkServerAccess("server.config.get");

     final String key = channel.readString();
     final OGlobalConfiguration cfg = OGlobalConfiguration.findByKey(key);
     String cfgValue = cfg != null ? cfg.getValueAsString() : "";

     beginResponse();
     try {
       sendOk(clientTxId);
       channel.writeString(cfgValue);
     } finally {
       endResponse();
     }
   }

   protected void commit() throws IOException {
     setDataCommandInfo("Transaction commit");

     if (!isConnectionAlive())
       return;

     final OTransactionOptimisticProxy tx = new OTransactionOptimisticProxy((ODatabaseRecordTx) connection.database.getUnderlying(),
         channel, connection.data.protocolVersion, this);

     try {
       connection.database.begin(tx);

       try {
         connection.database.commit();
         beginResponse();
         try {
           sendOk(clientTxId);

           // SEND BACK ALL THE RECORD IDS FOR THE CREATED RECORDS
           channel.writeInt(tx.getCreatedRecords().size());
           for (Entry<ORecordId, ORecord> entry : tx.getCreatedRecords().entrySet()) {
             channel.writeRID(entry.getKey());
             channel.writeRID(entry.getValue().getIdentity());

             // IF THE NEW OBJECT HAS VERSION > 0 MEANS THAT HAS BEEN UPDATED IN THE SAME TX. THIS HAPPENS FOR GRAPHS
             if (entry.getValue().getRecordVersion().getCounter() > 0)
               tx.getUpdatedRecords().put((ORecordId) entry.getValue().getIdentity(), entry.getValue());
           }

           // SEND BACK ALL THE NEW VERSIONS FOR THE UPDATED RECORDS
           channel.writeInt(tx.getUpdatedRecords().size());
           for (Entry<ORecordId, ORecord> entry : tx.getUpdatedRecords().entrySet()) {
             channel.writeRID(entry.getKey());
             channel.writeVersion(entry.getValue().getRecordVersion());
           }

           if (connection.data.protocolVersion >= 20)
             sendCollectionChanges();
         } finally {
           endResponse();
         }
       } catch (Exception e) {
         if (connection != null && connection.database != null) {
           if (connection.database.getTransaction().isActive())
             connection.database.rollback(true);

           final OSBTreeCollectionManager collectionManager = connection.database.getSbTreeCollectionManager();
           if (collectionManager != null)
             collectionManager.clearChangedIds();
         }

         sendErrorOrDropConnection(clientTxId, e);
       }
     } catch (OTransactionAbortedException e) {
       // TX ABORTED BY THE CLIENT
     } catch (Exception e) {
       // Error during TX initialization, possibly index constraints violation.
       if (tx.isActive())
         tx.rollback(true, -1);

       sendErrorOrDropConnection(clientTxId, e);
     }
   }

   protected void command() throws IOException {
     setDataCommandInfo("Execute remote command");

     final boolean asynch = channel.readByte() == 'a';
     String dbSerializerName = connection.database.getSerializer().toString();
     String name = getRecordSerializerName();

     if (!dbSerializerName.equals(name)) {
       ORecordSerializer ser = ORecordSerializerFactory.instance().getFormat(name);
       ONetworkThreadLocalSerializer.setNetworkSerializer(ser);
     }
     final OCommandRequestText command = (OCommandRequestText) OStreamSerializerAnyStreamable.INSTANCE.fromStream(channel
         .readBytes());
     ONetworkThreadLocalSerializer.setNetworkSerializer(null);

     connection.data.commandDetail = command.getText();

     // ENABLES THE CACHE TO IMPROVE PERFORMANCE OF COMPLEX COMMANDS LIKE TRAVERSE
     // connection.database.getLocalCache().setEnable(true);
     beginResponse();
     try {
       final OAbstractCommandResultListener listener;

       if (asynch) {
         listener = new OAsyncCommandResultListener(this, clientTxId, command.getResultListener());
         command.setResultListener(listener);
       } else
         listener = new OSyncCommandResultListener();

       final long serverTimeout = OGlobalConfiguration.COMMAND_TIMEOUT.getValueAsLong();

       if (serverTimeout > 0 && command.getTimeoutTime() > serverTimeout)
         // FORCE THE SERVER'S TIMEOUT
         command.setTimeout(serverTimeout, command.getTimeoutStrategy());

       if (!isConnectionAlive())
         return;

       // ASSIGNED THE PARSED FETCHPLAN
       listener.setFetchPlan(connection.database.command(command).getFetchPlan());

       final Object result = connection.database.command(command).execute();

       // FETCHPLAN HAS TO BE ASSIGNED AGAIN, because it can be changed by SQL statement
       listener.setFetchPlan(command.getFetchPlan());

       if (asynch) {
         // ASYNCHRONOUS
         if (listener.isEmpty())
           try {
             sendOk(clientTxId);
           } catch (IOException ignored) {
           }
         channel.writeByte((byte) 0); // NO MORE RECORDS

       } else {
         // SYNCHRONOUS
         sendOk(clientTxId);

         if (result == null) {
           // NULL VALUE
           channel.writeByte((byte) 'n');
         } else if (result instanceof OIdentifiable) {
           // RECORD
           channel.writeByte((byte) 'r');
           listener.result(result);
           writeIdentifiable((OIdentifiable) result);
         } else if (result instanceof ODocumentWrapper) {
           // RECORD
           channel.writeByte((byte) 'r');
           final ODocument doc = ((ODocumentWrapper) result).getDocument();
           listener.result(doc);
           writeIdentifiable(doc);
         } else if (OMultiValue.isMultiValue(result)) {
           channel.writeByte((byte) 'l');
           channel.writeInt(OMultiValue.getSize(result));
           for (Object o : OMultiValue.getMultiValueIterable(result)) {
             try {
               listener.result(o);
               writeIdentifiable((OIdentifiable) o);
             } catch (Exception e) {
               OLogManager.instance().warn(this, "Cannot serialize record: " + o);
               // WRITE NULL RECORD TO AVOID BREAKING PROTOCOL
               writeIdentifiable(null);
             }
           }
         } else {
           // ANY OTHER (INCLUDING LITERALS)
           channel.writeByte((byte) 'a');
           final StringBuilder value = new StringBuilder(64);
           listener.result(result);
           ORecordSerializerStringAbstract.fieldTypeToString(value, OType.getTypeByClass(result.getClass()), result);
           channel.writeString(value.toString());
         }

         if (connection.data.protocolVersion >= 17) {
           // SEND FETCHED RECORDS TO LOAD IN CLIENT CACHE
           for (ORecord rec : ((OSyncCommandResultListener) listener).getFetchedRecordsToSend()) {
             channel.writeByte((byte) 2); // CLIENT CACHE RECORD. IT
             // ISN'T PART OF THE
             // RESULT SET
             writeIdentifiable(rec);
           }

           channel.writeByte((byte) 0); // NO MORE RECORDS
         }
       }

     } finally {
       endResponse();
     }
   }

   protected void deleteRecord() throws IOException {
     setDataCommandInfo("Delete record");

     if (!isConnectionAlive())
       return;

     final ORID rid = channel.readRID();
     final ORecordVersion version = channel.readVersion();
     final byte mode = channel.readByte();

     final int result = deleteRecord(connection.database, rid, version);

     if (mode < 2) {
       beginResponse();
       try {
         sendOk(clientTxId);
         channel.writeByte((byte) result);
       } finally {
         endResponse();
       }
     }
   }

   protected void hideRecord() throws IOException {
     setDataCommandInfo("Hide record");

     if (!isConnectionAlive())
       return;

     final ORID rid = channel.readRID();
     final byte mode = channel.readByte();

     final int result = hideRecord(connection.database, rid);

     if (mode < 2) {
       beginResponse();
       try {
         sendOk(clientTxId);
         channel.writeByte((byte) result);
       } finally {
         endResponse();
       }
     }
   }

   protected void cleanOutRecord() throws IOException {
     setDataCommandInfo("Clean out record");

     if (!isConnectionAlive())
       return;

     final ORID rid = channel.readRID();
     final ORecordVersion version = channel.readVersion();
     final byte mode = channel.readByte();

     final int result = cleanOutRecord(connection.database, rid, version);

     if (mode < 2) {
       beginResponse();
       try {
         sendOk(clientTxId);
         channel.writeByte((byte) result);
       } finally {
         endResponse();
       }
     }
   }

   /**
    * VERSION MANAGEMENT:<br>
    * -1 : DOCUMENT UPDATE, NO VERSION CONTROL<br>
    * -2 : DOCUMENT UPDATE, NO VERSION CONTROL, NO VERSION INCREMENT<br>
    * -3 : DOCUMENT ROLLBACK, DECREMENT VERSION<br>
    * >-1 : MVCC CONTROL, RECORD UPDATE AND VERSION INCREMENT<br>
    * <-3 : WRONG VERSION VALUE
    *
    * @throws IOException
    */
   protected void updateRecord() throws IOException {
     setDataCommandInfo("Update record");

     if (!isConnectionAlive())
       return;

     final ORecordId rid = channel.readRID();
     boolean updateContent = true;
     if (connection.data.protocolVersion >= 23)
       updateContent = channel.readBoolean();
     final byte[] buffer = channel.readBytes();
     final ORecordVersion version = channel.readVersion();
     final byte recordType = channel.readByte();
     final byte mode = channel.readByte();

     final ORecordVersion newVersion = updateRecord(connection.database, rid, buffer, version, recordType, updateContent);

     if (mode < 2) {
       beginResponse();
       try {
         sendOk(clientTxId);
         channel.writeVersion(newVersion);

         if (connection.data.protocolVersion >= 20)
           sendCollectionChanges();
       } finally {
         endResponse();
       }
     }
   }

   protected void createRecord() throws IOException {
     setDataCommandInfo("Create record");

     if (!isConnectionAlive())
       return;

     final int dataSegmentId = connection.data.protocolVersion >= 10 && connection.data.protocolVersion < 24 ? channel.readInt() : 0;

     final ORecordId rid = new ORecordId(channel.readShort(), ORID.CLUSTER_POS_INVALID);
     final byte[] buffer = channel.readBytes();
     final byte recordType = channel.readByte();
     final byte mode = channel.readByte();

     final ORecord record = createRecord(connection.database, rid, buffer, recordType);

     if (mode < 2) {
       beginResponse();
       try {
         sendOk(clientTxId);
         channel.writeClusterPosition(record.getIdentity().getClusterPosition());
         if (connection.data.protocolVersion >= 11)
           channel.writeVersion(record.getRecordVersion());

         if (connection.data.protocolVersion >= 20)
           sendCollectionChanges();
       } finally {
         endResponse();
       }
     }
   }

   protected void readRecordMetadata() throws IOException {
     setDataCommandInfo("Record metadata");

     final ORID rid = channel.readRID();

     beginResponse();
     try {
       final ORecordMetadata metadata = connection.database.getRecordMetadata(rid);
       sendOk(clientTxId);
       channel.writeRID(metadata.getRecordId());
       channel.writeVersion(metadata.getRecordVersion());
     } finally {
       endResponse();
     }
   }

   protected void readRecord() throws IOException {
     setDataCommandInfo("Load record");

     if (!isConnectionAlive())
       return;

     final ORecordId rid = channel.readRID();
     final String fetchPlanString = channel.readString();
     boolean ignoreCache = false;
     if (connection.data.protocolVersion >= 9)
       ignoreCache = channel.readByte() == 1;

     boolean loadTombstones = false;
     if (connection.data.protocolVersion >= 13)
       loadTombstones = channel.readByte() > 0;

     if (rid.clusterId == 0 && rid.clusterPosition.longValue() == 0) {
       // @COMPATIBILITY 0.9.25
       // SEND THE DB CONFIGURATION INSTEAD SINCE IT WAS ON RECORD 0:0
       OFetchHelper.checkFetchPlanValid(fetchPlanString);

       beginResponse();
       try {
         sendOk(clientTxId);
         channel.writeByte((byte) 1);
         channel.writeBytes(connection.database.getStorage().getConfiguration().toStream());
         channel.writeVersion(OVersionFactory.instance().createVersion());
         channel.writeByte(ORecordBytes.RECORD_TYPE);
         channel.writeByte((byte) 0); // NO MORE RECORDS
       } finally {
         endResponse();
       }

     } else {
       final ORecord record = connection.database.load(rid, fetchPlanString, ignoreCache, loadTombstones,
           OStorage.LOCKING_STRATEGY.DEFAULT);

       beginResponse();
       try {
         sendOk(clientTxId);

         if (record != null) {
           channel.writeByte((byte) 1); // HAS RECORD
           channel.writeBytes(getRecordBytes(record));
           channel.writeVersion(record.getRecordVersion());
           channel.writeByte(ORecordInternal.getRecordType(record));

           if (fetchPlanString.length() > 0) {
             // BUILD THE SERVER SIDE RECORD TO ACCES TO THE FETCH
             // PLAN
             if (record instanceof ODocument) {
               final Map<String, Integer> fetchPlan = OFetchHelper.buildFetchPlan(fetchPlanString);

               final Set<ORecord> recordsToSend = new HashSet<ORecord>();
               final ODocument doc = (ODocument) record;
               final OFetchListener listener = new ORemoteFetchListener() {
                 @Override
                 protected void sendRecord(ORecord iLinked) {
                   recordsToSend.add(iLinked);
                 }
               };
               final OFetchContext context = new ORemoteFetchContext();
               OFetchHelper.fetch(doc, doc, fetchPlan, listener, context, "");

               // SEND RECORDS TO LOAD IN CLIENT CACHE
               for (ORecord d : recordsToSend) {
                 if (d.getIdentity().isValid()) {
                   channel.writeByte((byte) 2); // CLIENT CACHE
                   // RECORD. IT ISN'T PART OF THE RESULT SET
                   writeIdentifiable(d);
                 }
               }
             }

           }
         }
         channel.writeByte((byte) 0); // NO MORE RECORDS

       } finally {
         endResponse();
       }
     }
   }

   protected void beginResponse() {
     channel.acquireWriteLock();
   }

   protected void endResponse() throws IOException {
     // resetting transaction state. Commands are stateless and connection should be cleared
     // otherwise reused connection (connections pool) may lead to unpredicted errors
     if (connection != null && connection.database != null && connection.database.getTransaction() != null)
       connection.database.getTransaction().rollback();
     channel.flush();
     channel.releaseWriteLock();
   }

   protected void setDataCommandInfo(final String iCommandInfo) {
     if (connection != null)
       connection.data.commandInfo = iCommandInfo;
   }

   protected void readConnectionData() throws IOException {
     connection.data.driverName = channel.readString();
     connection.data.driverVersion = channel.readString();
     connection.data.protocolVersion = channel.readShort();
     connection.data.clientId = channel.readString();
     if (connection.data.protocolVersion > OChannelBinaryProtocol.PROTOCOL_VERSION_21)
       connection.data.serializationImpl = channel.readString();
     else
       connection.data.serializationImpl = ORecordSerializerSchemaAware2CSV.NAME;
   }

   protected void sendOk(final int iClientTxId) throws IOException {
     channel.writeByte(OChannelBinaryProtocol.RESPONSE_STATUS_OK);
     channel.writeInt(iClientTxId);
   }

   @Override
   protected void handleConnectionError(final OChannelBinaryServer iChannel, final Throwable e) {
     super.handleConnectionError(channel, e);
     OServerPluginHelper.invokeHandlerCallbackOnClientError(server, connection, e);
   }

   protected void sendResponse(final ODocument iResponse) throws IOException {
     beginResponse();
     try {
       sendOk(clientTxId);
       channel.writeBytes(iResponse != null ? iResponse.toStream() : null);
     } finally {
       endResponse();
     }
   }

   protected void freezeDatabase() throws IOException {
     setDataCommandInfo("Freeze database");
     String dbName = channel.readString();

     checkServerAccess("database.freeze");

     final String storageType;

     if (connection.data.protocolVersion >= 16)
       storageType = channel.readString();
     else
       storageType = "local";

     connection.database = getDatabaseInstance(dbName, ODatabaseDocument.TYPE, storageType);

     if (connection.database.exists()) {
       OLogManager.instance().info(this, "Freezing database '%s'", connection.database.getURL());

       if (connection.database.isClosed())
         openDatabase(connection.database, connection.serverUser.name, connection.serverUser.password);

       connection.database.freeze(true);
     } else {
       throw new OStorageException("Database with name '" + dbName + "' doesn't exits.");
     }

     beginResponse();
     try {
       sendOk(clientTxId);
     } finally {
       endResponse();
     }
   }

   protected void releaseDatabase() throws IOException {
     setDataCommandInfo("Release database");
     String dbName = channel.readString();

     checkServerAccess("database.release");

     final String storageType;
     if (connection.data.protocolVersion >= 16)
       storageType = channel.readString();
     else
       storageType = "local";

     connection.database = getDatabaseInstance(dbName, ODatabaseDocument.TYPE, storageType);

     if (connection.database.exists()) {
       OLogManager.instance().info(this, "Realising database '%s'", connection.database.getURL());

       if (connection.database.isClosed())
         openDatabase(connection.database, connection.serverUser.name, connection.serverUser.password);

       connection.database.release();
     } else {
       throw new OStorageException("Database with name '" + dbName + "' doesn't exits.");
     }

     beginResponse();
     try {
       sendOk(clientTxId);
     } finally {
       endResponse();
     }
   }

   protected void freezeCluster() throws IOException {
     setDataCommandInfo("Freeze cluster");
     final String dbName = channel.readString();
     final int clusterId = channel.readShort();

     checkServerAccess("database.freeze");

     final String storageType;

     if (connection.data.protocolVersion >= 16)
       storageType = channel.readString();
     else
       storageType = "local";

     connection.database = getDatabaseInstance(dbName, ODatabaseDocument.TYPE, storageType);

     if (connection.database.exists()) {
       OLogManager.instance().info(this, "Freezing database '%s' cluster %d", connection.database.getURL(), clusterId);

       if (connection.database.isClosed()) {
         openDatabase(connection.database, connection.serverUser.name, connection.serverUser.password);
       }

       connection.database.freezeCluster(clusterId);
     } else {
       throw new OStorageException("Database with name '" + dbName + "' doesn't exits.");
     }

     beginResponse();
     try {
       sendOk(clientTxId);
     } finally {
       endResponse();
     }
   }

   protected void releaseCluster() throws IOException {
     setDataCommandInfo("Release database");
     final String dbName = channel.readString();
     final int clusterId = channel.readShort();

     checkServerAccess("database.release");

     final String storageType;
     if (connection.data.protocolVersion >= 16)
       storageType = channel.readString();
     else
       storageType = "local";

     connection.database = getDatabaseInstance(dbName, ODatabaseDocument.TYPE, storageType);

     if (connection.database.exists()) {
       OLogManager.instance().info(this, "Realising database '%s' cluster %d", connection.database.getURL(), clusterId);

       if (connection.database.isClosed()) {
         openDatabase(connection.database, connection.serverUser.name, connection.serverUser.password);
       }

       connection.database.releaseCluster(clusterId);
     } else {
       throw new OStorageException("Database with name '" + dbName + "' doesn't exits.");
     }

     beginResponse();
     try {
       sendOk(clientTxId);
     } finally {
       endResponse();
     }
   }

   @Override
   protected String getRecordSerializerName() {
     return connection.data.serializationImpl;
   }

   private void sendErrorDetails(Throwable current) throws IOException {
     while (current != null) {
       // MORE DETAILS ARE COMING AS EXCEPTION
       channel.writeByte((byte) 1);

       channel.writeString(current.getClass().getName());
       channel.writeString(current.getMessage());

       current = current.getCause();
     }
     channel.writeByte((byte) 0);
   }

   private void serializeExceptionObject(Throwable original) throws IOException {
     try {
       final OMemoryStream memoryStream = new OMemoryStream();
       final ObjectOutputStream objectOutputStream = new ObjectOutputStream(memoryStream);

       objectOutputStream.writeObject(original);
       objectOutputStream.flush();

       final byte[] result = memoryStream.toByteArray();
       objectOutputStream.close();

       channel.writeBytes(result);
     } catch (Exception e) {
       OLogManager.instance().warn(this, "Can't serialize an exception object", e);

       // Write empty stream for binary compatibility
       channel.writeBytes(new byte[0]);
     }
   }

   /**
    * Due to protocol thread is daemon, shutdown should be executed in separate thread to guarantee its complete execution.
    *
    * This method never returns normally.
    */
   private void runShutdownInNonDaemonThread() {
     Thread shutdownThread = new Thread("OrientDB server shutdown thread") {
       public void run() {
         server.shutdown();
         ShutdownHelper.shutdown(1);
       }
     };
     shutdownThread.setDaemon(false);
     shutdownThread.start();
     try {
       shutdownThread.join();
     } catch (InterruptedException ignored) {
     }
   }

   private void ridBagSize() throws IOException {
     setDataCommandInfo("RidBag get size");

     OBonsaiCollectionPointer collectionPointer = OCollectionNetworkSerializer.INSTANCE.readCollectionPointer(channel);
     final byte[] changeStream = channel.readBytes();

     final OSBTreeCollectionManager sbTreeCollectionManager = connection.database.getSbTreeCollectionManager();
     final OSBTreeBonsai<OIdentifiable, Integer> tree = sbTreeCollectionManager.loadSBTree(collectionPointer);
     try {
       final Map<OIdentifiable, OSBTreeRidBag.Change> changes = OSBTreeRidBag.ChangeSerializationHelper.INSTANCE.deserializeChanges(
           changeStream, 0);

       int realSize = tree.getRealBagSize(changes);

       beginResponse();
       try {
         sendOk(clientTxId);
         channel.writeInt(realSize);
       } finally {
         endResponse();
       }
     } finally {
       sbTreeCollectionManager.releaseSBTree(collectionPointer);
     }
   }

   private void sbTreeBonsaiGetEntriesMajor() throws IOException {
     setDataCommandInfo("SB-Tree bonsai get values major");

     OBonsaiCollectionPointer collectionPointer = OCollectionNetworkSerializer.INSTANCE.readCollectionPointer(channel);
     byte[] keyStream = channel.readBytes();
     boolean inclusive = channel.readBoolean();
     int pageSize = 128;

     if (connection.data.protocolVersion >= 21)
       pageSize = channel.readInt();

     final OSBTreeCollectionManager sbTreeCollectionManager = connection.database.getSbTreeCollectionManager();
     final OSBTreeBonsai<OIdentifiable, Integer> tree = sbTreeCollectionManager.loadSBTree(collectionPointer);
     try {
       final OBinarySerializer<OIdentifiable> keySerializer = tree.getKeySerializer();
       OIdentifiable key = keySerializer.deserialize(keyStream, 0);

       final OBinarySerializer<Integer> valueSerializer = tree.getValueSerializer();

       OTreeInternal.AccumulativeListener<OIdentifiable, Integer> listener = new OTreeInternal.AccumulativeListener<OIdentifiable, Integer>(
           pageSize);
       tree.loadEntriesMajor(key, inclusive, true, listener);
       List<Entry<OIdentifiable, Integer>> result = listener.getResult();
       byte[] stream = serializeSBTreeEntryCollection(result, keySerializer, valueSerializer);

       beginResponse();
       try {
         sendOk(clientTxId);
         channel.writeBytes(stream);
       } finally {
         endResponse();
       }
     } finally {
       sbTreeCollectionManager.releaseSBTree(collectionPointer);
     }
   }

   private byte[] serializeSBTreeEntryCollection(List<Entry<OIdentifiable, Integer>> collection,
       OBinarySerializer<OIdentifiable> keySerializer, OBinarySerializer<Integer> valueSerializer) {
     byte[] stream = new byte[OIntegerSerializer.INT_SIZE + collection.size()
         * (keySerializer.getFixedLength() + valueSerializer.getFixedLength())];
     int offset = 0;

     OIntegerSerializer.INSTANCE.serializeLiteral(collection.size(), stream, offset);
     offset += OIntegerSerializer.INT_SIZE;

     for (Entry<OIdentifiable, Integer> entry : collection) {
       keySerializer.serialize(entry.getKey(), stream, offset);
       offset += keySerializer.getObjectSize(entry.getKey());

       valueSerializer.serialize(entry.getValue(), stream, offset);
       offset += valueSerializer.getObjectSize(entry.getValue());
     }
     return stream;
   }

   private void sbTreeBonsaiFirstKey() throws IOException {
     setDataCommandInfo("SB-Tree bonsai get first key");

     OBonsaiCollectionPointer collectionPointer = OCollectionNetworkSerializer.INSTANCE.readCollectionPointer(channel);

     final OSBTreeCollectionManager sbTreeCollectionManager = connection.database.getSbTreeCollectionManager();
     final OSBTreeBonsai<OIdentifiable, Integer> tree = sbTreeCollectionManager.loadSBTree(collectionPointer);
     try {
       OIdentifiable result = tree.firstKey();
       final OBinarySerializer<? super OIdentifiable> keySerializer;
       if (result == null) {
         keySerializer = ONullSerializer.INSTANCE;
       } else {
         keySerializer = tree.getKeySerializer();
       }

       byte[] stream = new byte[OByteSerializer.BYTE_SIZE + keySerializer.getObjectSize(result)];
       OByteSerializer.INSTANCE.serialize(keySerializer.getId(), stream, 0);
       keySerializer.serialize(result, stream, OByteSerializer.BYTE_SIZE);

       beginResponse();
       try {
         sendOk(clientTxId);
         channel.writeBytes(stream);
       } finally {
         endResponse();
       }
     } finally {
       sbTreeCollectionManager.releaseSBTree(collectionPointer);
     }
   }

   private void sbTreeBonsaiGet() throws IOException {
     setDataCommandInfo("SB-Tree bonsai get");

     OBonsaiCollectionPointer collectionPointer = OCollectionNetworkSerializer.INSTANCE.readCollectionPointer(channel);
     final byte[] keyStream = channel.readBytes();

     final OSBTreeCollectionManager sbTreeCollectionManager = connection.database.getSbTreeCollectionManager();
     final OSBTreeBonsai<OIdentifiable, Integer> tree = sbTreeCollectionManager.loadSBTree(collectionPointer);
     try {
       final OIdentifiable key = tree.getKeySerializer().deserialize(keyStream, 0);

       Integer result = tree.get(key);
       final OBinarySerializer<? super Integer> valueSerializer;
       if (result == null) {
         valueSerializer = ONullSerializer.INSTANCE;
       } else {
         valueSerializer = tree.getValueSerializer();
       }

       byte[] stream = new byte[OByteSerializer.BYTE_SIZE + valueSerializer.getObjectSize(result)];
       OByteSerializer.INSTANCE.serialize(valueSerializer.getId(), stream, 0);
       valueSerializer.serialize(result, stream, OByteSerializer.BYTE_SIZE);

       beginResponse();
       try {
         sendOk(clientTxId);
         channel.writeBytes(stream);
       } finally {
         endResponse();
       }
     } finally {
       sbTreeCollectionManager.releaseSBTree(collectionPointer);
     }
   }

   private void createSBTreeBonsai() throws IOException {
     setDataCommandInfo("Create SB-Tree bonsai instance");

     int clusterId = channel.readInt();

     OBonsaiCollectionPointer collectionPointer = connection.database.getSbTreeCollectionManager().createSBTree(clusterId, null);

     beginResponse();
     try {
       sendOk(clientTxId);
       OCollectionNetworkSerializer.INSTANCE.writeCollectionPointer(channel, collectionPointer);
     } finally {
       endResponse();
     }
   }

   private void lowerPositions() throws IOException {
     setDataCommandInfo("Retrieve lower positions");

     final int clusterId = channel.readInt();
     final OClusterPosition clusterPosition = channel.readClusterPosition();

     beginResponse();
     try {
       sendOk(clientTxId);

       final OPhysicalPosition[] previousPositions = connection.database.getStorage().lowerPhysicalPositions(clusterId,
           new OPhysicalPosition(clusterPosition));

       if (previousPositions != null) {
         channel.writeInt(previousPositions.length);

         for (final OPhysicalPosition physicalPosition : previousPositions) {
           channel.writeClusterPosition(physicalPosition.clusterPosition);
           channel.writeInt(physicalPosition.recordSize);
           channel.writeVersion(physicalPosition.recordVersion);
         }

       } else {
         channel.writeInt(0); // NO MORE RECORDS
       }

     } finally {
       endResponse();
     }
   }

   private void floorPositions() throws IOException {
     setDataCommandInfo("Retrieve floor positions");

     final int clusterId = channel.readInt();
     final OClusterPosition clusterPosition = channel.readClusterPosition();

     beginResponse();
     try {
       sendOk(clientTxId);

       final OPhysicalPosition[] previousPositions = connection.database.getStorage().floorPhysicalPositions(clusterId,
           new OPhysicalPosition(clusterPosition));

       if (previousPositions != null) {
         channel.writeInt(previousPositions.length);

         for (final OPhysicalPosition physicalPosition : previousPositions) {
           channel.writeClusterPosition(physicalPosition.clusterPosition);
           channel.writeInt(physicalPosition.recordSize);
           channel.writeVersion(physicalPosition.recordVersion);
         }

       } else {
         channel.writeInt(0); // NO MORE RECORDS
       }

     } finally {
       endResponse();
     }
   }

   private void higherPositions() throws IOException {
     setDataCommandInfo("Retrieve higher positions");

     final int clusterId = channel.readInt();
     final OClusterPosition clusterPosition = channel.readClusterPosition();

     beginResponse();
     try {
       sendOk(clientTxId);

       OPhysicalPosition[] nextPositions = connection.database.getStorage().higherPhysicalPositions(clusterId,
           new OPhysicalPosition(clusterPosition));

       if (nextPositions != null) {

         channel.writeInt(nextPositions.length);
         for (final OPhysicalPosition physicalPosition : nextPositions) {
           channel.writeClusterPosition(physicalPosition.clusterPosition);
           channel.writeInt(physicalPosition.recordSize);
           channel.writeVersion(physicalPosition.recordVersion);
         }
       } else {
         channel.writeInt(0); // NO MORE RECORDS
       }
     } finally {
       endResponse();
     }
   }

   private void ceilingPositions() throws IOException {
     setDataCommandInfo("Retrieve ceiling positions");

     final int clusterId = channel.readInt();
     final OClusterPosition clusterPosition = channel.readClusterPosition();

     beginResponse();
     try {
       sendOk(clientTxId);

       final OPhysicalPosition[] previousPositions = connection.database.getStorage().ceilingPhysicalPositions(clusterId,
           new OPhysicalPosition(clusterPosition));

       if (previousPositions != null) {
         channel.writeInt(previousPositions.length);

         for (final OPhysicalPosition physicalPosition : previousPositions) {
           channel.writeClusterPosition(physicalPosition.clusterPosition);
           channel.writeInt(physicalPosition.recordSize);
           channel.writeVersion(physicalPosition.recordVersion);
         }

       } else {
         channel.writeInt(0); // NO MORE RECORDS
       }

     } finally {
       endResponse();
     }
   }

   private boolean isConnectionAlive() {
     if (connection == null || connection.database == null) {
       // CONNECTION/DATABASE CLOSED, KILL IT
       OClientConnectionManager.instance().kill(connection);
       return false;
     }
     return true;
   }

   private void sendCollectionChanges() throws IOException {
     OSBTreeCollectionManager collectionManager = connection.database.getSbTreeCollectionManager();
     if (collectionManager != null) {
       Map<UUID, OBonsaiCollectionPointer> changedIds = collectionManager.changedIds();

       channel.writeInt(changedIds.size());

       for (Entry<UUID, OBonsaiCollectionPointer> entry : changedIds.entrySet()) {
         UUID id = entry.getKey();
         channel.writeLong(id.getMostSignificantBits());
         channel.writeLong(id.getLeastSignificantBits());

         OCollectionNetworkSerializer.INSTANCE.writeCollectionPointer(channel, entry.getValue());
       }
       collectionManager.clearChangedIds();
     }
   }

   private void sendDatabaseInformation() throws IOException {
     final Collection<? extends OCluster> clusters = connection.database.getStorage().getClusterInstances();
     int clusterCount = 0;
     for (OCluster c : clusters) {
       if (c != null) {
         ++clusterCount;
       }
     }
     if (connection.data.protocolVersion >= 7)
       channel.writeShort((short) clusterCount);
     else
       channel.writeInt(clusterCount);

     for (OCluster c : clusters) {
       if (c != null) {
         channel.writeString(c.getName());
         channel.writeShort((short) c.getId());

         if (connection.data.protocolVersion >= 12 && connection.data.protocolVersion < 24) {
           channel.writeString("none");
           channel.writeShort((short) -1);
         }
       }
     }
   }

   private void listDatabases() throws IOException {
     checkServerAccess("server.dblist");
     final ODocument result = new ODocument();
     result.field("databases", server.getAvailableStorageNames());

     setDataCommandInfo("List databases");

     beginResponse();
     try {
       sendOk(clientTxId);
       byte[] stream = getRecordBytes(result);
       channel.writeBytes(stream);
     } finally {
       endResponse();
     }
   }

   private boolean loadUserFromSchema(final String iUserName, final String iUserPassword) {
     account = connection.database.getMetadata().getSecurity().authenticate(iUserName, iUserPassword);
     return true;
   }

}
TOP

Related Classes of com.orientechnologies.orient.server.network.protocol.binary.ONetworkProtocolBinary

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.