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

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

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

import java.io.EOFException;
import java.io.IOException;
import java.net.Socket;
import java.net.SocketException;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import com.orientechnologies.common.concur.lock.OLockException;
import com.orientechnologies.common.exception.OException;
import com.orientechnologies.common.log.OLogManager;
import com.orientechnologies.orient.core.Orient;
import com.orientechnologies.orient.core.command.OCommandRequestInternal;
import com.orientechnologies.orient.core.command.OCommandRequestText;
import com.orientechnologies.orient.core.command.OCommandResultListener;
import com.orientechnologies.orient.core.config.OContextConfiguration;
import com.orientechnologies.orient.core.config.OGlobalConfiguration;
import com.orientechnologies.orient.core.db.ODatabaseComplex;
import com.orientechnologies.orient.core.db.ODatabaseRecordThreadLocal;
import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx;
import com.orientechnologies.orient.core.db.raw.ODatabaseRaw;
import com.orientechnologies.orient.core.db.record.ODatabaseRecordTx;
import com.orientechnologies.orient.core.db.record.OIdentifiable;
import com.orientechnologies.orient.core.engine.local.OEngineLocal;
import com.orientechnologies.orient.core.engine.memory.OEngineMemory;
import com.orientechnologies.orient.core.exception.ODatabaseException;
import com.orientechnologies.orient.core.exception.ORecordNotFoundException;
import com.orientechnologies.orient.core.exception.OSecurityAccessException;
import com.orientechnologies.orient.core.exception.OSerializationException;
import com.orientechnologies.orient.core.fetch.OFetchHelper;
import com.orientechnologies.orient.core.fetch.OFetchListener;
import com.orientechnologies.orient.core.id.ORID;
import com.orientechnologies.orient.core.id.ORecordId;
import com.orientechnologies.orient.core.metadata.schema.OSchemaProxy;
import com.orientechnologies.orient.core.metadata.schema.OType;
import com.orientechnologies.orient.core.metadata.security.ORole;
import com.orientechnologies.orient.core.metadata.security.OUser;
import com.orientechnologies.orient.core.query.OQuery;
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.serializer.record.OSerializationThreadLocal;
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.OStorage;
import com.orientechnologies.orient.core.storage.OStorageEmbedded;
import com.orientechnologies.orient.core.storage.impl.local.OStorageLocal;
import com.orientechnologies.orient.core.storage.impl.memory.OStorageMemory;
import com.orientechnologies.orient.enterprise.channel.OChannel;
import com.orientechnologies.orient.enterprise.channel.binary.OChannelBinaryProtocol;
import com.orientechnologies.orient.enterprise.channel.binary.OChannelBinaryServer;
import com.orientechnologies.orient.enterprise.channel.binary.ONetworkProtocolException;
import com.orientechnologies.orient.server.OClientConnection;
import com.orientechnologies.orient.server.OClientConnectionManager;
import com.orientechnologies.orient.server.OServer;
import com.orientechnologies.orient.server.OServerMain;
import com.orientechnologies.orient.server.config.OServerUserConfiguration;
import com.orientechnologies.orient.server.handler.OServerHandlerHelper;
import com.orientechnologies.orient.server.network.protocol.ONetworkProtocol;
import com.orientechnologies.orient.server.tx.OTransactionOptimisticProxy;

public class ONetworkProtocolBinary extends ONetworkProtocol {
  protected OClientConnection        connection;
  protected OChannelBinaryServer    channel;
  protected OUser                    account;

  protected String                  user;
  protected String                  passwd;
  protected int                      lastRequestType;
  protected int                      lastClientTxId;
  private OServerUserConfiguration  serverUser;
  private boolean                    firstConnection  = true;

  public ONetworkProtocolBinary() {
    super(Orient.getThreadGroup(), "IO-Binary");
  }

  public ONetworkProtocolBinary(final String iThreadName) {
    super(Orient.getThreadGroup(), iThreadName);
  }

  @Override
  public void config(final OServer iServer, final Socket iSocket, final OClientConnection iConnection,
      final OContextConfiguration iConfig) throws IOException {
    server = iServer;
    channel = new OChannelBinaryServer(iSocket, iConfig);
    connection = iConnection;

    // SEND PROTOCOL VERSION
    channel.writeShort((short) OChannelBinaryProtocol.CURRENT_PROTOCOL_VERSION);
    channel.flush();

    start();
  }

  @Override
  protected void execute() throws Exception {
    lastRequestType = -1;
    data.commandInfo = "Listening";
    data.commandDetail = "-";

    lastClientTxId = 0;

    try {
      lastRequestType = channel.readByte();
      lastClientTxId = channel.readInt();

      if (lastClientTxId > -1)
        connection = OClientConnectionManager.instance().getConnection(lastClientTxId);
      else if (firstConnection)
        firstConnection = false;
      else {
        if (connection == null) {
          sendShutdown();
          return;
        } else
          connection = OClientConnectionManager.instance().connect(connection.protocol.getChannel().socket, this);
      }

      if (connection != null)
        ODatabaseRecordThreadLocal.INSTANCE.set(connection.database);

      ++data.totalRequests;

      data.lastCommandReceived = System.currentTimeMillis();

      OServerHandlerHelper.invokeHandlerCallbackOnBeforeClientRequest(connection, (byte) lastRequestType);

      parseCommand();

      OServerHandlerHelper.invokeHandlerCallbackOnAfterClientRequest(connection, (byte) lastRequestType);

    } catch (EOFException e) {
      handleConnectionError(e);
      sendShutdown();
    } catch (SocketException e) {
      handleConnectionError(e);
      sendShutdown();
    } catch (OException e) {
      sendError(lastClientTxId, e);
    } catch (RuntimeException e) {
      sendError(lastClientTxId, e);
    } catch (Throwable t) {
      OLogManager.instance().error(this, "Error on executing request", t);
      sendError(lastClientTxId, t);
    } finally {
      try {
        channel.flush();
      } catch (Throwable t) {
        OLogManager.instance().debug(this, "Error on send data over the network", t);
      }

      OSerializationThreadLocal.INSTANCE.get().clear();

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

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

  @SuppressWarnings("unchecked")
  protected void parseCommand() throws IOException, InterruptedException {
    switch (lastRequestType) {

    case OChannelBinaryProtocol.REQUEST_SHUTDOWN: {
      data.commandInfo = "Shutdowning";

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

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

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

        channel.acquireExclusiveLock();
        try {
          sendOk(lastClientTxId);
        } finally {
          channel.releaseExclusiveLock();
        }
        channel.flush();
        channel.close();
        OServerMain.server().shutdown();
        System.exit(0);
        return;
      }

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

      sendError(lastClientTxId, new OSecurityAccessException("Invalid user/password to shutdown the server"));
      break;
    }

    case OChannelBinaryProtocol.REQUEST_CONNECT: {
      data.commandInfo = "Connect";

      serverLogin(channel.readString(), channel.readString());

      channel.acquireExclusiveLock();
      try {
        sendOk(lastClientTxId);
        channel.writeInt(connection.id);
      } finally {
        channel.releaseExclusiveLock();
      }
      break;
    }

    case OChannelBinaryProtocol.REQUEST_DB_OPEN: {
      data.commandInfo = "Open database";

      String dbURL = channel.readString();

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

      openDatabase(dbURL, user, passwd);

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

        channel.acquireExclusiveLock();
        try {
          sendOk(lastClientTxId);
          channel.writeInt(connection.id);

          sendDatabaseInformation();

          if (getClass().equals(ONetworkProtocolBinary.class))
            // NO EXTENSIONS (CLUSTER): SEND NULL DOCUMENT
            channel.writeBytes(null);

        } finally {
          channel.releaseExclusiveLock();
        }
      }

      break;
    }
    case OChannelBinaryProtocol.REQUEST_DB_RELOAD: {
      data.commandInfo = "Reload database information";

      channel.acquireExclusiveLock();
      try {
        sendOk(lastClientTxId);

        sendDatabaseInformation();

      } finally {
        channel.releaseExclusiveLock();
      }

      break;
    }
    case OChannelBinaryProtocol.REQUEST_DB_CREATE: {
      data.commandInfo = "Create database";

      String dbName = channel.readString();
      String storageMode = channel.readString();

      checkServerAccess("database.create");
      connection.database = getDatabaseInstance(dbName, storageMode);
      createDatabase(connection.database, null, null);

      channel.acquireExclusiveLock();
      try {
        sendOk(lastClientTxId);
      } finally {
        channel.releaseExclusiveLock();
      }
      break;
    }

    case OChannelBinaryProtocol.REQUEST_DB_CLOSE:
      data.commandInfo = "Close Database";

      if (connection != null) {
        connection.close();

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

      channel.acquireExclusiveLock();
      try {
        sendOk(lastClientTxId);
      } finally {
        channel.releaseExclusiveLock();
      }
      break;

    case OChannelBinaryProtocol.REQUEST_DB_EXIST: {
      data.commandInfo = "Exists database";
      String dbName = channel.readString();

      checkServerAccess("database.exists");

      connection.database = getDatabaseInstance(dbName, "local");

      channel.acquireExclusiveLock();
      try {
        sendOk(lastClientTxId);
        channel.writeByte((byte) (connection.database.exists() ? 1 : 0));
      } finally {
        channel.releaseExclusiveLock();
      }
      break;
    }

    case OChannelBinaryProtocol.REQUEST_DB_DELETE: {
      data.commandInfo = "Delete database";
      String dbName = channel.readString();

      checkServerAccess("database.delete");

      connection.database = getDatabaseInstance(dbName, "local");

      OLogManager.instance().info(this, "Dropped database '%s", connection.database.getURL());

      connection.database.delete();

      channel.acquireExclusiveLock();
      try {
        sendOk(lastClientTxId);
      } finally {
        channel.releaseExclusiveLock();
      }
      break;
    }

    case OChannelBinaryProtocol.REQUEST_DB_SIZE: {
      data.commandInfo = "Database size";

      channel.acquireExclusiveLock();
      try {
        sendOk(lastClientTxId);
        channel.writeLong(connection.database.getStorage().getSize());
      } finally {
        channel.releaseExclusiveLock();
      }
      break;
    }

    case OChannelBinaryProtocol.REQUEST_DB_COUNTRECORDS: {
      data.commandInfo = "Database count records";

      channel.acquireExclusiveLock();
      try {
        sendOk(lastClientTxId);
        channel.writeLong(connection.database.getStorage().countRecords());
      } finally {
        channel.releaseExclusiveLock();
      }
      break;
    }

    case OChannelBinaryProtocol.REQUEST_DATACLUSTER_COUNT: {
      data.commandInfo = "Count cluster elements";

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

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

      channel.acquireExclusiveLock();
      try {
        sendOk(lastClientTxId);
        channel.writeLong(count);
      } finally {
        channel.releaseExclusiveLock();
      }
      break;
    }

    case OChannelBinaryProtocol.REQUEST_DATACLUSTER_DATARANGE: {
      data.commandInfo = "Get the begin/end range of data in cluster";

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

      channel.acquireExclusiveLock();
      try {
        sendOk(lastClientTxId);
        channel.writeLong(pos[0]);
        channel.writeLong(pos[1]);
      } finally {
        channel.releaseExclusiveLock();
      }
      break;
    }

    case OChannelBinaryProtocol.REQUEST_DATACLUSTER_ADD: {
      data.commandInfo = "Add cluster";

      final String type = channel.readString();
      final String name = channel.readString();

      final int num;
      OStorage.CLUSTER_TYPE t = OStorage.CLUSTER_TYPE.valueOf(type);
      switch (t) {
      case PHYSICAL:
        num = connection.database.addPhysicalCluster(name, channel.readString(), channel.readInt());
        break;

      case MEMORY:
        num = connection.database.getStorage().addCluster(name, t);
        break;

      case LOGICAL:
        num = connection.database.addLogicalCluster(name, channel.readInt());
        break;

      default:
        throw new IllegalArgumentException("Cluster type " + type + " is not supported");
      }

      channel.acquireExclusiveLock();
      try {
        sendOk(lastClientTxId);
        channel.writeShort((short) num);
      } finally {
        channel.releaseExclusiveLock();
      }
      break;
    }

    case OChannelBinaryProtocol.REQUEST_DATACLUSTER_REMOVE: {
      data.commandInfo = "Remove cluster";

      final int id = channel.readShort();

      boolean result = connection.database.dropCluster(connection.database.getClusterNameById(id));

      channel.acquireExclusiveLock();
      try {
        sendOk(lastClientTxId);
        channel.writeByte((byte) (result ? 1 : 0));
      } finally {
        channel.releaseExclusiveLock();
      }
      break;
    }

    case OChannelBinaryProtocol.REQUEST_RECORD_LOAD: {
      data.commandInfo = "Load record";

      final ORecordId rid = channel.readRID();
      final String fetchPlanString = channel.readString();
      if (rid.clusterId == 0 && rid.clusterPosition == 0) {
        // @COMPATIBILITY 0.9.25
        // SEND THE DB CONFIGURATION INSTEAD SINCE IT WAS ON RECORD 0:0
        OFetchHelper.checkFetchPlanValid(fetchPlanString);
        channel.acquireExclusiveLock();
        try {
          sendOk(lastClientTxId);
          channel.writeByte((byte) 1);
          channel.writeBytes(connection.database.getStorage().getConfiguration().toStream());
          channel.writeInt(0);
          channel.writeByte(ORecordBytes.RECORD_TYPE);
        } finally {
          channel.releaseExclusiveLock();
        }
      } else {
        final ORecordInternal<?> record = connection.database.load(rid, fetchPlanString);

        // if (rid.equals(((OSchemaImpl) connection.database.getMetadata().getSchema()).getDocument().getIdentity()))
        // connection.database.getMetadata().getSchema().reload();

        channel.acquireExclusiveLock();
        try {
          sendOk(lastClientTxId);

          if (record != null) {
            channel.writeByte((byte) 1);
            channel.writeBytes(record.toStream());
            channel.writeInt(record.getVersion());
            channel.writeByte(record.getRecordType());

            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<ODocument> recordsToSend = new HashSet<ODocument>();
                OFetchHelper.fetch((ODocument) record, record, fetchPlan, null, 0, -1, new OFetchListener() {
                  @Override
                  public int size() {
                    return recordsToSend.size();
                  }

                  // ADD TO THE SET OF OBJECTS TO SEND
                  @Override
                  public Object fetchLinked(final ODocument iRoot, final Object iUserObject, final String iFieldName,
                      final Object iLinked) {
                    if (iLinked instanceof ODocument) {
                      if (((ODocument) iLinked).getIdentity().isValid())
                        return recordsToSend.add((ODocument) iLinked) ? iLinked : null;
                      return null;
                    } else if (iLinked instanceof Collection<?>)
                      return recordsToSend.addAll((Collection<? extends ODocument>) iLinked) ? iLinked : null;
                    else if (iLinked instanceof Map<?, ?>)
                      return recordsToSend.addAll(((Map<String, ? extends ODocument>) iLinked).values()) ? iLinked : null;
                    else
                      throw new IllegalArgumentException("Unrecognized type while fetching records: " + iLinked);
                  }
                });

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

            }
          }
        } finally {
          channel.releaseExclusiveLock();
        }
      }
      channel.writeByte((byte) 0); // NO MORE RECORDS
      break;
    }

    case OChannelBinaryProtocol.REQUEST_RECORD_CREATE: {
      data.commandInfo = "Create record";

      final ORecordId rid = new ORecordId(channel.readShort(), ORID.CLUSTER_POS_INVALID);

      final byte[] buffer = channel.readBytes();
      final byte recordType = channel.readByte();

      final ORecordInternal<?> record = Orient.instance().getRecordFactoryManager().newInstance(connection.database, recordType);
      record.fill(connection.database, rid, 0, buffer, true);
      connection.database.save(record);

      channel.acquireExclusiveLock();
      try {
        sendOk(lastClientTxId);
        channel.writeLong(record.getIdentity().getClusterPosition());
      } finally {
        channel.releaseExclusiveLock();
      }
      break;
    }

    case OChannelBinaryProtocol.REQUEST_RECORD_UPDATE: {
      data.commandInfo = "Update record";

      final ORecordId rid = channel.readRID();

      final byte[] buffer = channel.readBytes();
      final int version = channel.readInt();
      final byte recordType = channel.readByte();
      final ORecordInternal<?> newRecord = Orient.instance().getRecordFactoryManager().newInstance(connection.database, recordType);
      newRecord.fill(connection.database, rid, version, buffer, true);

      if (((OSchemaProxy) connection.database.getMetadata().getSchema()).getIdentity().equals(rid))
        // || ((OIndexManagerImpl) connection.database.getMetadata().getIndexManager()).getDocument().getIdentity().equals(rid)) {
        throw new OSecurityAccessException("Can't update internal record " + rid);

      final ORecordInternal<?> currentRecord;
      if (newRecord instanceof ODocument) {
        currentRecord = connection.database.load(rid);

        if (currentRecord == null)
          throw new ORecordNotFoundException(rid.toString());

        final ODocument doc = (ODocument) currentRecord;
        doc.merge((ODocument) newRecord, false, false);

      } else
        currentRecord = newRecord;

      currentRecord.setVersion(version);

      connection.database.save(currentRecord);

      if (currentRecord.getIdentity().toString().equals(connection.database.getStorage().getConfiguration().indexMgrRecordId)) {
        // FORCE INDEX MANAGER UPDATE. THIS HAPPENS FOR DIRECT CHANGES FROM REMOTE LIKE IN GRAPH
        connection.database.getMetadata().getIndexManager().reload();
      }

      channel.acquireExclusiveLock();
      try {
        sendOk(lastClientTxId);
        channel.writeInt(currentRecord.getVersion());
      } finally {
        channel.releaseExclusiveLock();
      }
      break;
    }

    case OChannelBinaryProtocol.REQUEST_RECORD_DELETE: {
      data.commandInfo = "Delete record";
      ORecordInternal<?> record = connection.database.load(channel.readRID());
      record.setVersion(channel.readInt());
      record.delete();

      channel.acquireExclusiveLock();
      try {
        sendOk(lastClientTxId);
        channel.writeByte((byte) 1);
      } finally {
        channel.releaseExclusiveLock();
      }
      break;
    }

    case OChannelBinaryProtocol.REQUEST_COUNT: {
      data.commandInfo = "Count cluster records";

      final String clusterName = channel.readString();
      final long size = connection.database.countClusterElements(clusterName);

      channel.acquireExclusiveLock();
      try {
        sendOk(lastClientTxId);
        channel.writeLong(size);
      } finally {
        channel.releaseExclusiveLock();
      }
      break;
    }

    case OChannelBinaryProtocol.REQUEST_COMMAND: {
      data.commandInfo = "Execute remote command";

      final boolean asynch = channel.readByte() == 'a';

      final OCommandRequestText command = (OCommandRequestText) OStreamSerializerAnyStreamable.INSTANCE.fromStream(
          connection.database, channel.readBytes());

      final OQuery<?> query = (OQuery<?>) (command instanceof OQuery<?> ? command : null);

      data.commandDetail = command.getText();

      channel.acquireExclusiveLock();
      try {
        if (asynch) {
          // ASYNCHRONOUS
          final StringBuilder empty = new StringBuilder();
          final Set<ODocument> recordsToSend = new HashSet<ODocument>();
          final int txId = lastClientTxId;

          final Map<String, Integer> fetchPlan = query != null ? OFetchHelper.buildFetchPlan(query.getFetchPlan()) : null;
          command.setResultListener(new OCommandResultListener() {
            @Override
            public boolean result(final Object iRecord) {
              if (empty.length() == 0)
                try {
                  sendOk(txId);
                  empty.append("-");
                } catch (IOException e1) {
                }

              try {
                channel.writeByte((byte) 1); // ONE MORE RECORD
                writeIdentifiable((ORecordInternal<?>) iRecord);

                if (fetchPlan != null && iRecord instanceof ODocument) {
                  OFetchHelper.fetch((ODocument) iRecord, iRecord, fetchPlan, null, 0, -1, new OFetchListener() {
                    @Override
                    public int size() {
                      return recordsToSend.size();
                    }

                    // ADD TO THE SET OF OBJECT TO
                    // SEND
                    @Override
                    public Object fetchLinked(final ODocument iRoot, final Object iUserObject, final String iFieldName,
                        final Object iLinked) {
                      if (iLinked instanceof ODocument)
                        return recordsToSend.add((ODocument) iLinked) ? iLinked : null;
                      else if (iLinked instanceof Collection<?>)
                        return recordsToSend.addAll((Collection<? extends ODocument>) iLinked) ? iLinked : null;
                      else if (iLinked instanceof Map<?, ?>)
                        return recordsToSend.addAll(((Map<String, ? extends ODocument>) iLinked).values()) ? iLinked : null;
                      else
                        throw new IllegalArgumentException("Unrecognized type while fetching records: " + iLinked);
                    }
                  });
                }

              } catch (IOException e) {
                return false;
              }

              return true;
            }
          });

          ((OCommandRequestInternal) connection.database.command(command)).execute();

          if (empty.length() == 0)
            try {
              sendOk(lastClientTxId);
            } catch (IOException e1) {
            }

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

          channel.writeByte((byte) 0); // NO MORE RECORDS
        } else {
          // SYNCHRONOUS
          final Object result = ((OCommandRequestInternal) connection.database.command(command)).execute();

          sendOk(lastClientTxId);

          if (result == null) {
            // NULL VALUE
            channel.writeByte((byte) 'n');
          } else if (result instanceof OIdentifiable) {
            // RECORD
            channel.writeByte((byte) 'r');
            writeIdentifiable((OIdentifiable) result);
          } else if (result instanceof Collection<?>) {
            channel.writeByte((byte) 'l');
            final Collection<OIdentifiable> list = (Collection<OIdentifiable>) result;
            channel.writeInt(list.size());
            for (OIdentifiable o : list) {
              writeIdentifiable(o);
            }
          } else {
            // ANY OTHER (INCLUDING LITERALS)
            channel.writeByte((byte) 'a');
            final StringBuilder value = new StringBuilder();
            ORecordSerializerStringAbstract.fieldTypeToString(value, connection.database, OType.getTypeByClass(result.getClass()),
                result);
            channel.writeString(value.toString());
          }
        }
      } finally {
        channel.releaseExclusiveLock();
      }
      break;
    }

    case OChannelBinaryProtocol.REQUEST_TX_COMMIT: {
      data.commandInfo = "Transaction commit";

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

      connection.database.begin(tx);
      try {
        connection.database.commit();
        channel.acquireExclusiveLock();
        try {
          sendOk(lastClientTxId);

          // 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().getVersion() > 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.writeInt(entry.getValue().getVersion());
          }
        } finally {
          channel.releaseExclusiveLock();
        }
      } catch (Exception e) {
        connection.database.rollback();
        sendError(lastClientTxId, e);
      }

      break;
    }

    case OChannelBinaryProtocol.REQUEST_CONFIG_GET: {
      data.commandInfo = "Get config";

      checkServerAccess("server.config.get");

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

      channel.acquireExclusiveLock();
      try {
        sendOk(lastClientTxId);
        channel.writeString(cfgValue);
      } finally {
        channel.releaseExclusiveLock();
      }
      break;
    }

    case OChannelBinaryProtocol.REQUEST_CONFIG_SET: {
      data.commandInfo = "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);

      channel.acquireExclusiveLock();
      try {
        sendOk(lastClientTxId);
      } finally {
        channel.releaseExclusiveLock();
      }
      break;
    }

    case OChannelBinaryProtocol.REQUEST_CONFIG_LIST: {
      data.commandInfo = "List config";

      checkServerAccess("server.config.get");

      channel.acquireExclusiveLock();
      try {
        sendOk(lastClientTxId);

        channel.writeShort((short) OGlobalConfiguration.values().length);
        for (OGlobalConfiguration cfg : OGlobalConfiguration.values()) {
          channel.writeString(cfg.getKey());
          channel.writeString(cfg.getValueAsString() != null ? cfg.getValueAsString() : "");
        }
      } finally {
        channel.releaseExclusiveLock();
      }

      break;
    }

    default:
      data.commandInfo = "Command not supported";
      OLogManager.instance().error(this, "Request not supported. Code: " + lastRequestType);
      channel.clearInput();
      sendError(lastClientTxId, new ONetworkProtocolException("Request not supported. Code: " + lastRequestType));
    }
  }

  private void sendDatabaseInformation() throws IOException {
    channel.writeInt(connection.database.getClusterNames().size());
    for (OCluster c : (connection.database.getStorage()).getClusterInstances()) {
      if (c != null) {
        channel.writeString(c.getName());
        channel.writeInt(c.getId());
        channel.writeString(c.getType());
      }
    }
  }

  @Override
  public void startup() {
    OServerHandlerHelper.invokeHandlerCallbackOnClientConnection(connection);
  }

  @Override
  public void shutdown() {
    sendShutdown();
    channel.close();

    if (connection == null)
      return;

    OServerHandlerHelper.invokeHandlerCallbackOnClientDisconnection(connection);

    if (connection.database != null)
      connection.database.close();

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

  @Override
  public OChannel getChannel() {
    return channel;
  }

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

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

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

      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;

      while (current != null) {
        // MORE DETAILS ARE COMING AS EXCEPTION
        channel.writeByte((byte) 1);

        channel.writeString(current.getClass().getName());
        channel.writeString(current != null ? current.getMessage() : null);

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

      channel.clearInput();

    } finally {
      channel.releaseExclusiveLock();
    }
  }

  private void serverLogin(final String iUser, final String iPassword) {
    if (!OServerMain.server().authenticate(iUser, iPassword, "connect"))
      throw new OSecurityAccessException(
          "Wrong user/password to [connect] to the remote OrientDB Server instance. Get the user/password from the config/orientdb-server-config.xml file");

    serverUser = OServerMain.server().getUser(iUser);
  }

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

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

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

  /**
   * Write a OIdentifiable instance using this format:<br/>
   * - 2 bytes: class id [-2=no record, -3=rid, -1=no class id, > -1 = valid] <br/>
   * - 1 byte: record type [v,c,b] <br/>
   * - 2 bytes: cluster id <br/>
   * - 8 bytes: position in cluster <br/>
   * - 4 bytes: record version <br/>
   * - x bytes: record vontent <br/>
   *
   * @param o
   * @throws IOException
   */
  public void writeIdentifiable(final OIdentifiable o) throws IOException {
    if (o == null)
      channel.writeShort(OChannelBinaryProtocol.RECORD_NULL);
    else if (o instanceof ORecordId) {
      channel.writeShort(OChannelBinaryProtocol.RECORD_RID);
      channel.writeRID((ORID) o);
    } else {
      writeRecord((ORecordInternal<?>) o);
    }
  }

  private void writeRecord(final ORecordInternal<?> iRecord) throws IOException {
    channel.writeShort((short) 0);
    channel.writeByte(iRecord.getRecordType());
    channel.writeRID(iRecord.getIdentity());
    channel.writeInt(iRecord.getVersion());
    try {
      final byte[] stream = iRecord.toStream();

      // TRIM TAILING SPACES (DUE TO OVERSIZE)
      int realLength = stream.length;
      for (int i = stream.length - 1; i > -1; --i) {
        if (stream[i] == 32)
          --realLength;
        else
          break;
      }

      channel.writeBytes(stream, realLength);
    } catch (Exception e) {
      channel.writeBytes(null);
      OLogManager.instance().error(this, "Error on unmarshalling record #" + iRecord.getIdentity().toString(),
          OSerializationException.class);
    }
  }

  protected ODatabaseDocumentTx openDatabase(final String iDbUrl, final String iUser, final String iPassword)
      throws InterruptedException {
    final String path = OServerMain.server().getStoragePath(iDbUrl);

    connection.database = new ODatabaseDocumentTx(path);

    if (connection.database.isClosed())
      if (connection.database.getStorage() instanceof OStorageMemory)
        connection.database.create();
      else
        connection.database.open(iUser, iPassword);

    connection.rawDatabase = ((ODatabaseRaw) ((ODatabaseComplex<?>) connection.database.getUnderlying()).getUnderlying());

    return connection.database;
  }

  protected void createDatabase(final ODatabaseDocumentTx iDatabase, String dbUser, String dbPasswd) {
    if (iDatabase.exists())
      throw new ODatabaseException("Database '" + iDatabase.getURL() + "' already exists");

    for (OStorage stg : Orient.instance().getStorages()) {
      if (stg.getName().equalsIgnoreCase(iDatabase.getName()) && stg.exists())
        throw new ODatabaseException("Database '" + iDatabase.getURL() + "' already exists: " + stg);
    }

    iDatabase.create();
    if (dbUser != null) {

      OUser oUser = iDatabase.getMetadata().getSecurity().getUser(dbUser);
      if (oUser == null) {
        iDatabase.getMetadata().getSecurity().createUser(dbUser, dbPasswd, new String[] { ORole.ADMIN });
      } else {
        oUser.setPassword(dbPasswd);
        oUser.save();
      }
    }
    OLogManager.instance().info(this, "Created database '%s' of type '%s'", iDatabase.getURL(),
        iDatabase.getStorage() instanceof OStorageLocal ? "local" : "memory");

    if (iDatabase.getStorage() instanceof OStorageLocal) {
      // CLOSE IT BECAUSE IT WILL BE OPEN AT FIRST USE
      iDatabase.close();
    }

    connection.rawDatabase = ((ODatabaseRaw) ((ODatabaseComplex<?>) iDatabase.getUnderlying()).getUnderlying());
  }

  protected ODatabaseDocumentTx getDatabaseInstance(final String iDbName, final String iStorageMode) {
    final String path;

    final OStorage stg = Orient.instance().getStorage(iDbName);
    if (stg != null)
      path = stg.getURL();
    else if (iStorageMode.equals(OEngineLocal.NAME)) {
      path = iStorageMode + ":${ORIENTDB_HOME}/databases/" + iDbName;
    } else if (iStorageMode.equals(OEngineMemory.NAME)) {
      path = iStorageMode + ":" + iDbName;
    } else
      throw new IllegalArgumentException("Can't create database: storage mode '" + iStorageMode + "' is not supported.");

    return new ODatabaseDocumentTx(path);
  }

  private void handleConnectionError(Throwable e) {
    OServerHandlerHelper.invokeHandlerCallbackOnClientError(connection, e);
  }
}
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.