Package com.orientechnologies.orient.core.storage.impl.memory

Source Code of com.orientechnologies.orient.core.storage.impl.memory.OStorageMemory

/*
* 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.core.storage.impl.memory;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.orientechnologies.common.concur.lock.OLockManager.LOCK;
import com.orientechnologies.common.profiler.OProfiler;
import com.orientechnologies.orient.core.Orient;
import com.orientechnologies.orient.core.config.OStorageConfiguration;
import com.orientechnologies.orient.core.db.record.ODatabaseRecord;
import com.orientechnologies.orient.core.engine.memory.OEngineMemory;
import com.orientechnologies.orient.core.exception.OConcurrentModificationException;
import com.orientechnologies.orient.core.exception.ORecordNotFoundException;
import com.orientechnologies.orient.core.exception.OStorageException;
import com.orientechnologies.orient.core.id.ORID;
import com.orientechnologies.orient.core.id.ORecordId;
import com.orientechnologies.orient.core.record.ORecord;
import com.orientechnologies.orient.core.storage.OCluster;
import com.orientechnologies.orient.core.storage.OPhysicalPosition;
import com.orientechnologies.orient.core.storage.ORawBuffer;
import com.orientechnologies.orient.core.storage.ORecordBrowsingListener;
import com.orientechnologies.orient.core.storage.OStorage;
import com.orientechnologies.orient.core.storage.OStorageEmbedded;
import com.orientechnologies.orient.core.storage.impl.local.OStorageConfigurationSegment;
import com.orientechnologies.orient.core.tx.OTransaction;
import com.orientechnologies.orient.core.tx.OTransactionAbstract;
import com.orientechnologies.orient.core.tx.OTransactionRecordEntry;

/**
* Memory implementation of storage. This storage works only in memory and has the following features:
* <ul>
* <li>The name is "Memory"</li>
* <li>Has a unique Data Segment</li>
* </ul>
*
* @author Luca Garulli
*
*/
public class OStorageMemory extends OStorageEmbedded {
  private final ODataSegmentMemory    data              = new ODataSegmentMemory();
  private final List<OClusterMemory>  clusters          = new ArrayList<OClusterMemory>();
  private int                          defaultClusterId  = 0;

  public OStorageMemory(final String iURL) {
    super(iURL, OEngineMemory.NAME + ":" + iURL, "rw");
    configuration = new OStorageConfiguration(this);
  }

  public void create(final Map<String, Object> iOptions) {
    addUser();

    lock.acquireExclusiveLock();
    try {

      addDataSegment(OStorage.DATA_DEFAULT_NAME);

      // ADD THE METADATA CLUSTER TO STORE INTERNAL STUFF
      addCluster(OStorage.CLUSTER_INTERNAL_NAME, null);

      // ADD THE INDEX CLUSTER TO STORE, BY DEFAULT, ALL THE RECORDS OF INDEXING
      addCluster(OStorage.CLUSTER_INDEX_NAME, null);

      // ADD THE DEFAULT CLUSTER
      defaultClusterId = addCluster(OStorage.CLUSTER_DEFAULT_NAME, null);

      configuration.create();

      status = STATUS.OPEN;

    } catch (OStorageException e) {
      close();
      throw e;

    } catch (IOException e) {
      close();
      throw new OStorageException("Error on creation of storage: " + name, e);

    } finally {
      lock.releaseExclusiveLock();
    }
  }

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

    if (status == STATUS.OPEN)
      // ALREADY OPENED: THIS IS THE CASE WHEN A STORAGE INSTANCE IS
      // REUSED
      return;

    lock.acquireExclusiveLock();
    try {

      if (!exists())
        throw new OStorageException("Can't open the storage '" + name + "' because it not exists in path: " + url);

      status = STATUS.OPEN;

    } finally {
      lock.releaseExclusiveLock();
    }
  }

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

      if (!checkForClose(iForce))
        return;

      status = STATUS.CLOSING;

      // CLOSE ALL THE CLUSTERS
      for (OClusterMemory c : clusters)
        if (c != null)
          c.close();
      clusters.clear();

      // CLOSE THE DATA SEGMENT
      data.close();
      level2Cache.shutdown();

      super.close(iForce);

      Orient.instance().unregisterStorage(this);
      status = STATUS.CLOSED;

    } finally {
      lock.releaseExclusiveLock();
    }
  }

  public void delete() {
    close(true);
  }

  public void reload() {
  }

  public int addCluster(final String iClusterName, final OStorage.CLUSTER_TYPE iClusterType, final Object... iParameters) {
    lock.acquireExclusiveLock();
    try {
      int clusterId = clusters.size();
      for (int i = 0; i < clusters.size(); ++i) {
        if (clusters.get(i) == null) {
          clusterId = i;
          break;
        }
      }

      final OClusterMemory cluster = new OClusterMemory(clusterId, iClusterName.toLowerCase());

      if (clusterId == clusters.size())
        // APPEND IT
        clusters.add(cluster);
      else
        // RECYCLE THE FREE POSITION
        clusters.set(clusterId, cluster);

      return clusterId;

    } finally {
      lock.releaseExclusiveLock();
    }
  }

  public boolean dropCluster(final int iClusterId) {
    lock.acquireExclusiveLock();
    try {

      final OCluster c = clusters.get(iClusterId);
      if (c != null) {
        c.delete();
        clusters.set(iClusterId, null);
        getLevel2Cache().freeCluster(iClusterId);
      }

    } catch (IOException e) {
    } finally {

      lock.releaseExclusiveLock();
    }

    return false;
  }

  public int addDataSegment(final String iDataSegmentName) {
    // UNIQUE DATASEGMENT
    return 0;
  }

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

  public long createRecord(final ORecordId iRid, final byte[] iContent, final byte iRecordType) {
    final long timer = OProfiler.getInstance().startChrono();

    lock.acquireSharedLock();
    try {

      final long offset = data.createRecord(iContent);
      final OCluster cluster = getClusterById(iRid.clusterId);

      iRid.clusterPosition = cluster.addPhysicalPosition(0, offset, iRecordType);
      return iRid.clusterPosition;
    } catch (IOException e) {
      throw new OStorageException("Error on create record in cluster: " + iRid.clusterId, e);

    } finally {
      lock.releaseSharedLock();
      OProfiler.getInstance().stopChrono("OStorageMemory.createRecord", timer);
    }
  }

  public ORawBuffer readRecord(final ODatabaseRecord iDatabase, final ORecordId iRid, String iFetchPlan) {
    return readRecord(getClusterById(iRid.clusterId), iRid, true);
  }

  @Override
  protected ORawBuffer readRecord(final OCluster iClusterSegment, final ORecordId iRid, final boolean iAtomicLock) {
    final long timer = OProfiler.getInstance().startChrono();

    lock.acquireSharedLock();
    try {
      lockManager.acquireLock(Thread.currentThread(), iRid, LOCK.SHARED);

      final long lastPos = iClusterSegment.getLastEntryPosition();

      if (iRid.clusterPosition > lastPos)
        throw new ORecordNotFoundException("Record " + iRid + " is out cluster size. Valid range for cluster '"
            + iClusterSegment.getName() + "' is 0-" + lastPos);

      try {
        final OPhysicalPosition ppos = iClusterSegment.getPhysicalPosition(iRid.clusterPosition, new OPhysicalPosition());

        if (ppos == null)
          return null;

        return new ORawBuffer(data.readRecord(ppos.dataPosition), ppos.version, ppos.type);

      } finally {
        lockManager.releaseLock(Thread.currentThread(), iRid, LOCK.SHARED);
      }
    } catch (IOException e) {
      throw new OStorageException("Error on read record in cluster: " + iClusterSegment.getId(), e);

    } finally {
      lock.releaseSharedLock();
      OProfiler.getInstance().stopChrono("OStorageMemory.readRecord", timer);
    }
  }

  public int updateRecord(final ORecordId iRid, final byte[] iContent, final int iVersion, final byte iRecordType) {
    final long timer = OProfiler.getInstance().startChrono();

    final OCluster cluster = getClusterById(iRid.clusterId);

    lock.acquireSharedLock();
    try {

      lockManager.acquireLock(Thread.currentThread(), iRid, LOCK.EXCLUSIVE);
      try {

        final OPhysicalPosition ppos = cluster.getPhysicalPosition(iRid.clusterPosition, new OPhysicalPosition());
        if (ppos == null)
          return -1;

        // MVCC TRANSACTION: CHECK IF VERSION IS THE SAME
        if (iVersion > -1 && ppos.version != iVersion)
          throw new OConcurrentModificationException(
              "Can't update record "
                  + iRid
                  + " because the version is not the latest one. Probably you are updating an old record or it has been modified by another user (db=v"
                  + ppos.version + " your=v" + iVersion + ")");

        data.updateRecord(ppos.dataPosition, iContent);

        return ++(ppos.version);

      } finally {
        lockManager.releaseLock(Thread.currentThread(), iRid, LOCK.EXCLUSIVE);
      }
    } catch (IOException e) {
      throw new OStorageException("Error on update record " + iRid, e);

    } finally {
      lock.releaseSharedLock();
      OProfiler.getInstance().stopChrono("OStorageMemory.updateRecord", timer);
    }
  }

  public boolean deleteRecord(final ORecordId iRid, final int iVersion) {
    final long timer = OProfiler.getInstance().startChrono();

    final OCluster cluster = getClusterById(iRid.clusterId);

    lock.acquireSharedLock();
    try {

      lockManager.acquireLock(Thread.currentThread(), iRid, LOCK.EXCLUSIVE);
      try {

        final OPhysicalPosition ppos = cluster.getPhysicalPosition(iRid.clusterPosition, new OPhysicalPosition());

        if (ppos == null)
          return false;

        // MVCC TRANSACTION: CHECK IF VERSION IS THE SAME
        if (iVersion > -1 && ppos.version != iVersion)
          throw new OConcurrentModificationException(
              "Can't delete record "
                  + iRid
                  + " because the version is not the latest one. Probably you are deleting an old record or it has been modified by another user (db=v"
                  + ppos.version + " your=v" + iVersion + ")");

        cluster.removePhysicalPosition(iRid.clusterPosition, null);
        data.deleteRecord(ppos.dataPosition);

        return true;

      } finally {
        lockManager.releaseLock(Thread.currentThread(), iRid, LOCK.EXCLUSIVE);
      }

    } catch (IOException e) {
      throw new OStorageException("Error on delete record " + iRid, e);

    } finally {
      lock.releaseSharedLock();
      OProfiler.getInstance().stopChrono("OStorageMemory.deleteRecord", timer);
    }
  }

  public long count(final int iClusterId) {
    final OCluster cluster = getClusterById(iClusterId);

    lock.acquireSharedLock();
    try {

      return cluster.getEntries();

    } finally {
      lock.releaseSharedLock();
    }
  }

  public long[] getClusterDataRange(final int iClusterId) {
    final OCluster cluster = getClusterById(iClusterId);
    lock.acquireSharedLock();
    try {

      return new long[] { cluster.getFirstEntryPosition(), cluster.getLastEntryPosition() };

    } catch (IOException e) {
      throw new OStorageException("Error on getting last entry position in cluster: " + iClusterId, e);
    } finally {
      lock.releaseSharedLock();
    }
  }

  public long count(final int[] iClusterIds) {
    lock.acquireSharedLock();
    try {

      long tot = 0;
      for (int i = 0; i < iClusterIds.length; ++i) {
        final OCluster cluster = clusters.get(iClusterIds[i]);

        if (cluster != null)
          tot += cluster.getEntries();
      }
      return tot;

    } finally {
      lock.releaseSharedLock();
    }
  }

  public OCluster getClusterByName(final String iClusterName) {
    lock.acquireSharedLock();
    try {

      for (int i = 0; i < clusters.size(); ++i) {
        final OCluster cluster = clusters.get(i);

        if (cluster != null && cluster.getName().equalsIgnoreCase(iClusterName))
          return cluster;
      }
      return null;

    } finally {
      lock.releaseSharedLock();
    }
  }

  public int getClusterIdByName(String iClusterName) {
    iClusterName = iClusterName.toLowerCase();

    lock.acquireSharedLock();
    try {

      for (int i = 0; i < clusters.size(); ++i) {
        final OCluster cluster = clusters.get(i);

        if (cluster != null && cluster.getName().equalsIgnoreCase(iClusterName))
          return cluster.getId();
      }
      return -1;

    } finally {
      lock.releaseSharedLock();
    }
  }

  public String getClusterTypeByName(final String iClusterName) {
    return OClusterMemory.TYPE;
  }

  public String getPhysicalClusterNameById(final int iClusterId) {
    lock.acquireSharedLock();
    try {

      for (int i = 0; i < clusters.size(); ++i) {
        final OCluster cluster = clusters.get(i);

        if (cluster != null && cluster.getId() == iClusterId)
          return cluster.getName();
      }
      return null;

    } finally {
      lock.releaseSharedLock();
    }
  }

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

      Set<String> result = new HashSet<String>();
      for (int i = 0; i < clusters.size(); ++i) {
        final OCluster cluster = clusters.get(i);

        if (cluster != null)
          result.add(cluster.getName());
      }
      return result;

    } finally {
      lock.releaseSharedLock();
    }
  }

  public void commit(final OTransaction iTx) {
    lock.acquireExclusiveLock();
    try {

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

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

        iTx.clearRecordEntries();

        for (OTransactionRecordEntry txEntry : tmpEntries)
          // COMMIT ALL THE SINGLE ENTRIES ONE BY ONE
          commitEntry(((OTransaction) iTx).getId(), txEntry);

        tmpEntries.clear();
      }

      // UPDATE THE CACHE ONLY IF THE ITERATOR ALLOWS IT
      OTransactionAbstract.updateCacheFromEntries(this, iTx, iTx.getAllRecordEntries(), true);
    } catch (IOException e) {
      rollback(iTx);

    } finally {
      lock.releaseExclusiveLock();
    }
  }

  public void rollback(final OTransaction iTx) {
  }

  public void synch() {
  }

  public void browse(final int[] iClusterId, final ORecordBrowsingListener iListener, final ORecord<?> iRecord) {
  }

  public boolean exists() {
    lock.acquireSharedLock();
    try {

      return clusters.size() > 0;

    } finally {
      lock.releaseSharedLock();
    }
  }

  public OCluster getClusterById(int iClusterId) {
    lock.acquireSharedLock();
    try {

      if (iClusterId == ORID.CLUSTER_ID_INVALID)
        // GET THE DEFAULT CLUSTER
        iClusterId = defaultClusterId;

      return clusters.get(iClusterId);

    } finally {
      lock.releaseSharedLock();
    }
  }

  public int getClusters() {
    lock.acquireSharedLock();
    try {

      return clusters.size();

    } finally {
      lock.releaseSharedLock();
    }
  }

  public Collection<? extends OCluster> getClusterInstances() {
    lock.acquireSharedLock();
    try {

      return Collections.unmodifiableCollection(clusters);

    } finally {
      lock.releaseSharedLock();
    }
  }

  public int getDefaultClusterId() {
    return defaultClusterId;
  }

  public long getSize() {
    long size = 0;

    lock.acquireSharedLock();
    try {
      size += data.getSize();

      for (OClusterMemory c : clusters)
        if (c != null)
          size += c.getSize();

    } finally {
      lock.releaseSharedLock();
    }
    return size;
  }

  @Override
  public boolean checkForRecordValidity(final OPhysicalPosition ppos) {
    if (ppos.dataSegment > 0)
      return false;

    lock.acquireSharedLock();
    try {

      if (ppos.dataPosition >= data.count())
        return false;

    } finally {
      lock.releaseSharedLock();
    }
    return true;
  }

  private void commitEntry(final int iTxId, final OTransactionRecordEntry txEntry) throws IOException {

    final ORecordId rid = (ORecordId) txEntry.getRecord().getIdentity();

    final OCluster cluster = txEntry.clusterName != null ? getClusterByName(txEntry.clusterName) : getClusterById(rid.clusterId);
    rid.clusterId = cluster.getId();

    switch (txEntry.status) {
    case OTransactionRecordEntry.LOADED:
      break;

    case OTransactionRecordEntry.CREATED:
      if (rid.isNew()) {
        // CHECK 2 TIMES TO ASSURE THAT IT'S A CREATE OR AN UPDATE BASED ON RECURSIVE TO-STREAM METHOD
        final byte[] stream = txEntry.getRecord().toStream();

        if (rid.isNew()) {
          createRecord(rid, stream, txEntry.getRecord().getRecordType());
        } else {
          txEntry.getRecord().setVersion(
              updateRecord(rid, stream, txEntry.getRecord().getVersion(), txEntry.getRecord().getRecordType()));
        }
      }
      break;

    case OTransactionRecordEntry.UPDATED:
      txEntry.getRecord().setVersion(
          updateRecord(rid, txEntry.getRecord().toStream(), txEntry.getRecord().getVersion(), txEntry.getRecord().getRecordType()));
      break;

    case OTransactionRecordEntry.DELETED:
      deleteRecord(rid, txEntry.getRecord().getVersion());
      break;
    }
  }

  public OStorageConfigurationSegment getConfigurationSegment() {
    return null;
  }

  public void renameCluster(final String iOldName, final String iNewName) {
    final OClusterMemory cluster = (OClusterMemory) getClusterByName(iOldName);
    if (cluster != null)
      try {
        cluster.set(com.orientechnologies.orient.core.storage.OCluster.ATTRIBUTES.NAME, iNewName);
      } catch (IOException e) {
      }
  }
}
TOP

Related Classes of com.orientechnologies.orient.core.storage.impl.memory.OStorageMemory

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.