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.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.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.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.ORecordAbstract;
import com.orientechnologies.orient.core.record.ORecordFactory;
import com.orientechnologies.orient.core.record.ORecordInternal;
import com.orientechnologies.orient.core.record.ORecordSchemaAware;
import com.orientechnologies.orient.core.record.impl.ODocument;
import com.orientechnologies.orient.core.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.ODictionaryLocal;
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.db.OSharedDocumentDatabase;
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;

  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
        connection = OClientConnectionManager.instance().connect(connection.protocol.getChannel().socket, this);

      ++data.totalRequests;

      data.lastCommandReceived = System.currentTimeMillis();

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

      parseCommand();

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

    } catch (EOFException e) {
      handleError(e);
      sendShutdown();
    } catch (SocketException e) {
      handleError(e);
      sendShutdown();
    } catch (OException e) {
      handleError(e);
      sendError(lastClientTxId, e);
    } catch (RuntimeException e) {
      handleError(e);
      sendError(lastClientTxId, e);
    } catch (Throwable t) {
      handleError(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;
    }
  }

  private void handleError(Throwable e) {
    OServerHandlerHelper.invokeHandlerCallbackOnClientError(connection, e);
  }

  @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());

        sendOk(lastClientTxId);
        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());

      sendOk(lastClientTxId);
      channel.writeInt(connection.id);
      break;
    }

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

      String dbURL = channel.readString();
      String dbName = dbURL.substring(dbURL.lastIndexOf(":") + 1);

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

      openDatabase(dbName, user, passwd);

      if (!(connection.database.getStorage() instanceof OStorageEmbedded) && !loadUserFromSchema(user, passwd)) {
        sendError(lastClientTxId, new OSecurityAccessException(connection.database.getName(), "Access denied to database '"
            + connection.database.getName() + "' for user: " + user));
      } else {
        sendOk(lastClientTxId);
        channel.writeInt(connection.id);
        channel.writeInt(connection.database.getClusterNames().size());
        for (OCluster c : (connection.database.getStorage()).getClusters()) {
          if (c != null) {
            channel.writeString(c.getName());
            channel.writeInt(c.getId());
            channel.writeString(c.getType());
          }
        }

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

      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);

      sendOk(lastClientTxId);
      break;
    }

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

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

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

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

      checkServerAccess("database.exists");

      connection.database = getDatabaseInstance(dbName, "local");
      sendOk(lastClientTxId);
      channel.writeByte((byte) (connection.database.exists() ? 1 : 0));
      break;
    }

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

      checkServerAccess("database.delete");

      connection.database = getDatabaseInstance(dbName, "local");
      connection.database.delete();
      sendOk(lastClientTxId);
      break;
    }

    case OChannelBinaryProtocol.REQUEST_DB_SIZE: {
      data.commandInfo = "Database size";
      sendOk(lastClientTxId);
      channel.writeLong(connection.database.getStorage().getSize());
      break;
    }

    case OChannelBinaryProtocol.REQUEST_DB_COUNTRECORDS: {
      data.commandInfo = "Database count records";
      sendOk(lastClientTxId);
      channel.writeLong(connection.database.getStorage().countRecords());
      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();

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

      sendOk(lastClientTxId);
      channel.writeLong(count);
      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());

      sendOk(lastClientTxId);
      channel.writeLong(pos[0]);
      channel.writeLong(pos[1]);
      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");
      }

      sendOk(lastClientTxId);
      channel.writeShort((short) num);
      break;
    }

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

      final int id = channel.readShort();

      boolean result = connection.database.getStorage().removeCluster(id);

      sendOk(lastClientTxId);
      channel.writeByte((byte) (result ? 1 : 0));
      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
        sendOk(lastClientTxId);
        channel.writeByte((byte) 1);
        channel.writeBytes(connection.database.getStorage().getConfiguration().toStream());
        channel.writeInt(0);
        channel.writeByte(ORecordBytes.RECORD_TYPE);
      } else {
        final ORecordInternal<?> record = connection.database.load(rid);

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

        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
                  writeRecord(doc);
                }
              }
            }

          }
        }
      }
      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 = ORecordFactory.newInstance(recordType);
//      record.fill(connection.database, rid, 0, buffer);
//      connection.database.save(record);
       final long location = connection.rawDatabase.save(rid, buffer, -1, recordType);
      sendOk(lastClientTxId);
      //channel.writeLong(rid.getClusterPosition());
      channel.writeLong(location);
      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<?> record = ORecordFactory.newInstance(recordType);
      // record.fill(connection.database, rid, version, buffer);
      //
      // connection.database.save(record);

      long newVersion = connection.rawDatabase.save(rid, channel.readBytes(), channel.readInt(), channel.readByte());

      // TODO: Handle it by using triggers
      if (connection.database.getMetadata().getSchema().getDocument().getIdentity().equals(rid))
        connection.database.getMetadata().getSchema().reload();
      else if (connection.database.getMetadata().getIndexManager().getDocument().getIdentity().equals(rid))
        connection.database.getMetadata().getIndexManager().reload();
      else if (((ODictionaryLocal<?>) connection.database.getDictionary()).getTree().getRecord().getIdentity().equals(rid))
        ((ODictionaryLocal<?>) connection.database.getDictionary()).load();

      sendOk(lastClientTxId);

      channel.writeInt((int) newVersion);
      // channel.writeInt(record.getVersion());
      break;
    }

    case OChannelBinaryProtocol.REQUEST_RECORD_DELETE: {
      data.commandInfo = "Delete record";
      final ORecordId rid = new ORecordId(channel.readShort(), channel.readLong());
      connection.rawDatabase.delete(rid, channel.readInt());
      sendOk(lastClientTxId);

      channel.writeByte((byte) 1);
      break;
    }

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

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

      sendOk(lastClientTxId);

      channel.writeLong(size);
      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();

      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
              writeRecord((ORecordInternal<?>) iRecord);
              // TEMPORARY TEST: ON LINUX THIS SLOW DOWN A LOT
              // channel.flush();

              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
          writeRecord(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');
          channel.writeBytes(null);
        } else if (result instanceof ORecord<?>) {
          // RECORD
          channel.writeByte((byte) 'r');
          writeRecord((ORecordInternal<?>) 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) {
            writeRecord(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());
        }
      }
      break;
    }

    case OChannelBinaryProtocol.REQUEST_INDEX_LOOKUP: {
      data.commandInfo = "Index lookup";

      final String key = channel.readString();
      ORecordAbstract<?> value = (ORecordAbstract<?>) connection.database.getDictionary().get(key);

      if (value != null)
        value = ((ODatabaseRecordTx) connection.database.getUnderlying()).load(value);

      sendOk(lastClientTxId);

      writeRecord(value);
      break;
    }

    case OChannelBinaryProtocol.REQUEST_INDEX_PUT: {
      data.commandInfo = "Index put";

      String key = channel.readString();
      ORecordInternal<?> value = ORecordFactory.newInstance(channel.readByte());

      final ORecordId rid = channel.readRID();
      value.setIdentity(rid);
      value.setDatabase(connection.database);

      value = connection.database.getDictionary().putRecord(key, value);

      if (value != null)
        ((ODatabaseRecordTx) connection.database.getUnderlying()).load(value);

      sendOk(lastClientTxId);

      writeRecord(value);
      break;
    }

    case OChannelBinaryProtocol.REQUEST_INDEX_REMOVE: {
      data.commandInfo = "Index remove";

      final String key = channel.readString();
      final ORecordInternal<?> value = connection.database.getDictionary().remove(key);

      if (value != null)
        ((ODatabaseRecordTx) connection.database.getUnderlying()).load(value);

      sendOk(lastClientTxId);

      writeRecord(value);
      break;
    }

    case OChannelBinaryProtocol.REQUEST_INDEX_SIZE: {
      data.commandInfo = "Index size";

      sendOk(lastClientTxId);
      channel.writeInt(connection.database.getDictionary().size());
      break;
    }

    case OChannelBinaryProtocol.REQUEST_INDEX_KEYS: {
      data.commandInfo = "Index keys";

      sendOk(lastClientTxId);
      channel.writeCollectionString(connection.database.getDictionary().keySet());
      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();
        sendOk(lastClientTxId);

        // 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());
        }
      } 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() : "";

      sendOk(lastClientTxId);
      channel.writeString(cfgValue);
      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);

      sendOk(lastClientTxId);
      break;
    }

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

      checkServerAccess("server.config.get");

      sendOk(lastClientTxId);

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

      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));
    }
  }

  @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 {
    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();
  }

  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().getUser(iUserName);
    if (account == null)
      throw new OSecurityAccessException(connection.database.getName(), "User '" + iUserName + "' was not found in database: "
          + connection.database.getName());

    boolean allow = account.checkPassword(iUserPassword);

    if (!allow)
      account = null;

    return allow;
  }

  /**
   * Write a record using this format:<br/>
   * - 2 bytes: class id [-2=no record, -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
   */
  private void writeRecord(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 {
      channel
          .writeShort((short) (o instanceof ORecordSchemaAware<?> && ((ORecordSchemaAware<?>) o).getSchemaClass() != null ? ((ORecordSchemaAware<?>) o)
              .getSchemaClass().getId() : -1));

      ORecordInternal<?> rec = (ORecordInternal<?>) o;
      channel.writeByte(rec.getRecordType());
      channel.writeRID(rec.getIdentity());
      channel.writeInt(rec.getVersion());
      try {
        channel.writeBytes(rec.toStream());
      } catch (Exception e) {
        channel.writeBytes(null);
        OLogManager.instance().error(this, "Error on unmarshalling record #" + o.getIdentity().toString(),
            OSerializationException.class);
      }
    }
  }

  protected ODatabaseDocumentTx openDatabase(final String dbName, final String iUser, final String iPassword)
      throws InterruptedException {
    connection.database = OSharedDocumentDatabase.acquire(dbName, iUser, iPassword);

    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) {
    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();

    } else {
      // SAVE THE DB IN MEMORY
      OServerMain.server().getMemoryDatabases().put(iDatabase.getName(), iDatabase);
    }

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

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

    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);
  }
}
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.