From 8f2cc03841d940b77807123a7f356dfeaca6a896 Mon Sep 17 00:00:00 2001 From: Tglman Date: Thu, 21 Jul 2016 15:45:11 +0100 Subject: [PATCH] first implementation of backward compatible database delegate --- .../core/db/ODatabaseDocumentInternal.java | 4 + .../orient/core/db/OEmbeddedDBFactory.java | 20 +- .../core/db/OPartitionedDatabasePool.java | 3 +- .../document/ODatabaseDocumentEmbedded.java | 2 +- .../db/document/ODatabaseDocumentRemote.java | 2 +- .../core/db/document/ODatabaseDocumentTx.java | 4544 ++++------------- .../document/ODatabaseDocumentTxInternal.java | 4 +- .../db/document/ODatabaseDocumentTxOrig.java | 3532 +++++++++++++ .../document/ODatabaseDocumentTxPooled.java | 4 +- .../OCommandExecutorSQLDropPropertyTest.java | 3 +- ...DuplicateDictionaryIndexChangesTxTest.java | 2 +- .../DuplicateNonUniqueIndexChangesTxTest.java | 2 +- .../tx/DuplicateUniqueIndexChangesTxTest.java | 2 +- .../blueprints/impls/orient/OrientVertex.java | 2 +- .../test/database/auto/SQLSelectTest.java | 12 +- 15 files changed, 4569 insertions(+), 3569 deletions(-) mode change 100755 => 100644 core/src/main/java/com/orientechnologies/orient/core/db/document/ODatabaseDocumentTx.java create mode 100755 core/src/main/java/com/orientechnologies/orient/core/db/document/ODatabaseDocumentTxOrig.java diff --git a/core/src/main/java/com/orientechnologies/orient/core/db/ODatabaseDocumentInternal.java b/core/src/main/java/com/orientechnologies/orient/core/db/ODatabaseDocumentInternal.java index c001d0469bc..68fd4c4b551 100644 --- a/core/src/main/java/com/orientechnologies/orient/core/db/ODatabaseDocumentInternal.java +++ b/core/src/main/java/com/orientechnologies/orient/core/db/ODatabaseDocumentInternal.java @@ -36,6 +36,8 @@ import com.orientechnologies.orient.core.storage.ORecordCallback; import com.orientechnologies.orient.core.storage.OStorage; +import java.util.Set; + public interface ODatabaseDocumentInternal extends ODatabaseDocument, ODatabaseInternal { /** @@ -89,6 +91,8 @@ void executeDeleteRecord(OIdentifiable record, final int iVersion, final boolean ODatabaseDocumentInternal copy(); + Set executeReadRecords(final Set iRids, final boolean ignoreCache); + void checkIfActive(); void callOnOpenListeners(); diff --git a/core/src/main/java/com/orientechnologies/orient/core/db/OEmbeddedDBFactory.java b/core/src/main/java/com/orientechnologies/orient/core/db/OEmbeddedDBFactory.java index cdec9c926b3..7d01b872059 100644 --- a/core/src/main/java/com/orientechnologies/orient/core/db/OEmbeddedDBFactory.java +++ b/core/src/main/java/com/orientechnologies/orient/core/db/OEmbeddedDBFactory.java @@ -151,7 +151,7 @@ public synchronized void create(String name, String user, String password, Datab config = solveConfig(config); OAbstractPaginatedStorage storage; if (type == DatabaseType.MEMORY) { - storage = (OAbstractPaginatedStorage) memory.createStorage(buildName(name), new HashMap<>()); + storage = (OAbstractPaginatedStorage) memory.createStorage(name, new HashMap<>()); } else { storage = (OAbstractPaginatedStorage) disk.createStorage(buildName(name), new HashMap<>()); } @@ -196,24 +196,6 @@ private void internalCreate(OrientDBConfig config, OAbstractPaginatedStorage sto } } - private void internalCreate(OrientDBConfig config, OAbstractPaginatedStorage storage) { - storage.create(config.getConfigurations()); - ORecordSerializer serializer = ORecordSerializerFactory.instance().getDefaultRecordSerializer(); - if (serializer.toString().equals("ORecordDocument2csv")) - throw new ODatabaseException("Impossible to create the database with ORecordDocument2csv serializer"); - storage.getConfiguration().setRecordSerializer(serializer.toString()); - storage.getConfiguration().setRecordSerializerVersion(serializer.getCurrentVersion()); - // since 2.1 newly created databases use strict SQL validation by default - storage.getConfiguration().setProperty(OStatement.CUSTOM_STRICT_SQL, "true"); - - storage.getConfiguration().update(); - - try (final ODatabaseDocumentEmbedded embedded = new ODatabaseDocumentEmbedded(storage)) { - embedded.setSerializer(serializer); - embedded.internalCreate(config); - } - } - @Override public synchronized boolean exists(String name, String user, String password) { if (!storages.containsKey(name)) { diff --git a/core/src/main/java/com/orientechnologies/orient/core/db/OPartitionedDatabasePool.java b/core/src/main/java/com/orientechnologies/orient/core/db/OPartitionedDatabasePool.java index f5b0d369111..b5c6891b0c5 100755 --- a/core/src/main/java/com/orientechnologies/orient/core/db/OPartitionedDatabasePool.java +++ b/core/src/main/java/com/orientechnologies/orient/core/db/OPartitionedDatabasePool.java @@ -91,6 +91,7 @@ public class OPartitionedDatabasePool extends OOrientListenerAbstract implements private volatile boolean closed = false; private boolean autoCreate = false; + public OPartitionedDatabasePool(String url, String userName, String password) { this(url, userName, password, Runtime.getRuntime().availableProcessors(), -1); } @@ -412,7 +413,7 @@ private final class DatabaseDocumentTxPooled extends ODatabaseDocumentTx { private PoolPartition partition; private DatabaseDocumentTxPooled(String iURL) { - super(iURL, true, false); + super(iURL); } @Override diff --git a/core/src/main/java/com/orientechnologies/orient/core/db/document/ODatabaseDocumentEmbedded.java b/core/src/main/java/com/orientechnologies/orient/core/db/document/ODatabaseDocumentEmbedded.java index b83e2cf661b..178afa7b3c9 100644 --- a/core/src/main/java/com/orientechnologies/orient/core/db/document/ODatabaseDocumentEmbedded.java +++ b/core/src/main/java/com/orientechnologies/orient/core/db/document/ODatabaseDocumentEmbedded.java @@ -44,7 +44,7 @@ /** * Created by tglman on 27/06/16. */ -public class ODatabaseDocumentEmbedded extends ODatabaseDocumentTx { +public class ODatabaseDocumentEmbedded extends ODatabaseDocumentTxOrig { private OrientDBConfig config; diff --git a/core/src/main/java/com/orientechnologies/orient/core/db/document/ODatabaseDocumentRemote.java b/core/src/main/java/com/orientechnologies/orient/core/db/document/ODatabaseDocumentRemote.java index 0307adccb44..f4ace970611 100644 --- a/core/src/main/java/com/orientechnologies/orient/core/db/document/ODatabaseDocumentRemote.java +++ b/core/src/main/java/com/orientechnologies/orient/core/db/document/ODatabaseDocumentRemote.java @@ -42,7 +42,7 @@ /** * Created by tglman on 30/06/16. */ -public class ODatabaseDocumentRemote extends ODatabaseDocumentTx { +public class ODatabaseDocumentRemote extends ODatabaseDocumentTxOrig { private OrientDBConfig config; diff --git a/core/src/main/java/com/orientechnologies/orient/core/db/document/ODatabaseDocumentTx.java b/core/src/main/java/com/orientechnologies/orient/core/db/document/ODatabaseDocumentTx.java old mode 100755 new mode 100644 index 55d50b8c847..1311a32c4f7 --- a/core/src/main/java/com/orientechnologies/orient/core/db/document/ODatabaseDocumentTx.java +++ b/core/src/main/java/com/orientechnologies/orient/core/db/document/ODatabaseDocumentTx.java @@ -1,3532 +1,1012 @@ -/* - * - * * Copyright 2014 Orient Technologies LTD (info(at)orientechnologies.com) - * * - * * Licensed under the Apache License, Version 2.0 (the "License"); - * * you may not use this file except in compliance with the License. - * * You may obtain a copy of the License at - * * - * * http://www.apache.org/licenses/LICENSE-2.0 - * * - * * Unless required by applicable law or agreed to in writing, software - * * distributed under the License is distributed on an "AS IS" BASIS, - * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * * See the License for the specific language governing permissions and - * * limitations under the License. - * * - * * For more information: http://www.orientechnologies.com - * - */ - -package com.orientechnologies.orient.core.db.document; - -import com.orientechnologies.common.concur.ONeedRetryException; -import com.orientechnologies.common.exception.OException; -import com.orientechnologies.common.io.OIOUtils; -import com.orientechnologies.common.listener.OListenerManger; -import com.orientechnologies.common.log.OLogManager; -import com.orientechnologies.common.util.OCallable; -import com.orientechnologies.common.util.OCommonConst; -import com.orientechnologies.common.util.OPair; -import com.orientechnologies.orient.core.OUncompletedCommit; -import com.orientechnologies.orient.core.Orient; -import com.orientechnologies.orient.core.cache.OCommandCacheHook; -import com.orientechnologies.orient.core.cache.OLocalRecordCache; -import com.orientechnologies.orient.core.command.OCommandOutputListener; -import com.orientechnologies.orient.core.command.OCommandRequest; -import com.orientechnologies.orient.core.command.OCommandRequestInternal; -import com.orientechnologies.orient.core.config.OContextConfiguration; -import com.orientechnologies.orient.core.config.OGlobalConfiguration; -import com.orientechnologies.orient.core.conflict.ORecordConflictStrategy; -import com.orientechnologies.orient.core.db.*; -import com.orientechnologies.orient.core.db.record.*; -import com.orientechnologies.orient.core.db.record.ridbag.sbtree.ORidBagDeleter; -import com.orientechnologies.orient.core.db.record.ridbag.sbtree.OSBTreeCollectionManager; -import com.orientechnologies.orient.core.dictionary.ODictionary; -import com.orientechnologies.orient.core.exception.*; -import com.orientechnologies.orient.core.fetch.OFetchHelper; -import com.orientechnologies.orient.core.hook.ORecordHook; -import com.orientechnologies.orient.core.id.ORID; -import com.orientechnologies.orient.core.id.ORecordId; -import com.orientechnologies.orient.core.index.ClassIndexManagerRemote; -import com.orientechnologies.orient.core.index.OClassIndexManager; -import com.orientechnologies.orient.core.intent.OIntent; -import com.orientechnologies.orient.core.iterator.ORecordIteratorClass; -import com.orientechnologies.orient.core.iterator.ORecordIteratorCluster; -import com.orientechnologies.orient.core.metadata.OMetadata; -import com.orientechnologies.orient.core.metadata.OMetadataDefault; -import com.orientechnologies.orient.core.metadata.function.OFunctionTrigger; -import com.orientechnologies.orient.core.metadata.schema.OClass; -import com.orientechnologies.orient.core.metadata.schema.OSchemaProxy; -import com.orientechnologies.orient.core.metadata.security.*; -import com.orientechnologies.orient.core.metadata.sequence.OSequenceTrigger; -import com.orientechnologies.orient.core.query.OQuery; -import com.orientechnologies.orient.core.query.live.OLiveQueryHook; -import com.orientechnologies.orient.core.record.ORecord; -import com.orientechnologies.orient.core.record.ORecordInternal; -import com.orientechnologies.orient.core.record.ORecordVersionHelper; -import com.orientechnologies.orient.core.record.impl.OBlob; -import com.orientechnologies.orient.core.record.impl.ODirtyManager; -import com.orientechnologies.orient.core.record.impl.ODocument; -import com.orientechnologies.orient.core.record.impl.ODocumentInternal; -import com.orientechnologies.orient.core.schedule.OSchedulerTrigger; -import com.orientechnologies.orient.core.serialization.serializer.binary.OBinarySerializerFactory; -import com.orientechnologies.orient.core.serialization.serializer.record.ORecordSaveThreadLocal; -import com.orientechnologies.orient.core.serialization.serializer.record.ORecordSerializer; -import com.orientechnologies.orient.core.serialization.serializer.record.ORecordSerializerFactory; -import com.orientechnologies.orient.core.serialization.serializer.record.string.ORecordSerializerSchemaAware2CSV; -import com.orientechnologies.orient.core.sql.OCommandSQL; -import com.orientechnologies.orient.core.sql.executor.OTodoResultSet; -import com.orientechnologies.orient.core.sql.parser.OStatement; -import com.orientechnologies.orient.core.storage.*; -import com.orientechnologies.orient.core.storage.impl.local.OAbstractPaginatedStorage; -import com.orientechnologies.orient.core.storage.impl.local.OFreezableStorageComponent; -import com.orientechnologies.orient.core.storage.impl.local.paginated.OOfflineClusterException; -import com.orientechnologies.orient.core.storage.impl.local.paginated.ORecordSerializationContext; -import com.orientechnologies.orient.core.tx.OTransaction; -import com.orientechnologies.orient.core.tx.OTransactionNoTx; -import com.orientechnologies.orient.core.tx.OTransactionOptimistic; -import com.orientechnologies.orient.core.tx.OTransactionRealAbstract; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.text.SimpleDateFormat; -import java.util.*; -import java.util.concurrent.Callable; -import java.util.concurrent.atomic.AtomicReference; - -/** - * Document API entrypoint. - * - * @author Luca Garulli - */ -@SuppressWarnings("unchecked") -public class ODatabaseDocumentTx extends OListenerManger implements ODatabaseDocumentInternal { - - protected final Map properties = new HashMap(); - protected Map unmodifiableHooks; - protected final Set inHook = new HashSet(); - protected ORecordSerializer serializer; - protected String url; - protected OStorage storage; - protected STATUS status; - protected OIntent currentIntent; - protected ODatabaseInternal databaseOwner; - protected OMetadataDefault metadata; - protected OImmutableUser user; - protected byte recordType; - protected final Map hooks = new LinkedHashMap(); - protected boolean retainRecords = true; - protected OLocalRecordCache localCache; - protected boolean mvcc; - protected OCurrentStorageComponentsFactory componentsFactory; - protected boolean initialized = false; - protected OTransaction currentTx; - protected boolean keepStorageOpen = false; - protected final AtomicReference owner = new AtomicReference(); - protected boolean ownerProtection = true; - - protected ODatabaseSessionMetadata sessionMetadata; - - protected final ORecordHook[][] hooksByScope = new ORecordHook[ORecordHook.SCOPE.values().length][]; - protected OSharedContext sharedContext; - - private boolean prefetchRecords; - - protected ODatabaseDocumentTx() { - //DO NOTHING IS FOR EXTENDED OBJECTS - super(false); - } - - /** - * Creates a new connection to the database. - * - * @param iURL of the database - */ - public ODatabaseDocumentTx(final String iURL) { - this(iURL, false, true); - } - - public ODatabaseDocumentTx(final String iURL, boolean keepStorageOpen, boolean ownerProtection) { - super(false); - - this.ownerProtection = ownerProtection; - - if (iURL == null) - throw new IllegalArgumentException("URL parameter is null"); - - activateOnCurrentThread(); - - try { - this.keepStorageOpen = keepStorageOpen; - url = iURL.replace('\\', '/'); - status = STATUS.CLOSED; - - // SET DEFAULT PROPERTIES - setProperty("fetch-max", 50); - - storage = Orient.instance().loadStorage(url); - - // OVERWRITE THE URL - url = storage.getURL(); - - unmodifiableHooks = Collections.unmodifiableMap(hooks); - - recordType = ODocument.RECORD_TYPE; - localCache = new OLocalRecordCache(); - - mvcc = OGlobalConfiguration.DB_MVCC.getValueAsBoolean(); - - init(); - - databaseOwner = this; - } catch (Exception t) { - if (storage != null) - Orient.instance().unregisterStorage(storage); - ODatabaseRecordThreadLocal.INSTANCE.remove(); - - throw OException.wrapException(new ODatabaseException("Error on opening database '" + iURL + "'"), t); - } - - setSerializer(getDefaultSerializer()); - } - - /** - * @return default serializer which is used to serialize documents. Default serializer is common for all database instances. - */ - public static ORecordSerializer getDefaultSerializer() { - return ORecordSerializerFactory.instance().getDefaultRecordSerializer(); - } - - /** - * Sets default serializer. The default serializer is common for all database instances. - * - * @param iDefaultSerializer new default serializer value - */ - public static void setDefaultSerializer(ORecordSerializer iDefaultSerializer) { - ORecordSerializerFactory.instance().setDefaultRecordSerializer(iDefaultSerializer); - } - - /** - * Opens connection to the storage with given user and password. - *

- * But we do suggest {@link com.orientechnologies.orient.core.db.OPartitionedDatabasePool#acquire()} instead. It will make work - * faster even with embedded database. - * - * @param iUserName Username to login - * @param iUserPassword Password associated to the user - * @return Current database instance. - */ - @Override - public DB open(final String iUserName, final String iUserPassword) { - boolean failure = true; - setupThreadOwner(); - activateOnCurrentThread(); - try { - if (status == STATUS.OPEN) - throw new IllegalStateException("Database " + getName() + " is already open"); - - if (user != null && !user.getName().equals(iUserName)) - initialized = false; - - final String encKey = (String) getProperty(OGlobalConfiguration.STORAGE_ENCRYPTION_KEY.getKey()); - String currKey = null; - - if (storage.getConfiguration() != null && storage.getConfiguration().getContextConfiguration() != null) { - currKey = (String) storage.getConfiguration().getContextConfiguration() - .getValue(OGlobalConfiguration.STORAGE_ENCRYPTION_KEY); - - // If an encryption key is set as a database property, and - // the storage engine is open and has an encryption key value, and - // the two encryption keys differ, force the storage closed so that the - // new encryption key in properties will be used. - if (encKey != null && currKey != null && !encKey.equals(currKey)) { - // If the storage is open... - if (!storage.isClosed()) { - storage.close(true, false); // force it closed - } - } - } - - if (storage.isClosed()) { - OContextConfiguration conf = new OContextConfiguration(); - bindPropertiesToContext(conf, properties); - storage.open(iUserName, iUserPassword, conf); - initialized = false; - } else if (storage instanceof OStorageProxy) { - final String name = ((OStorageProxy) storage).getUserName(); - if (name == null || !name.equals(iUserName)) { - storage.close(); - OContextConfiguration conf = new OContextConfiguration(); - bindPropertiesToContext(conf, properties); - storage.open(iUserName, iUserPassword, conf); - } - } - - status = STATUS.OPEN; - - initAtFirstOpen(iUserName, iUserPassword); - - if (!(getStorage() instanceof OStorageProxy)) { - final OSecurity security = metadata.getSecurity(); - if (user == null || user.getVersion() != security.getVersion() || !user.getName().equalsIgnoreCase(iUserName)) { - final OUser usr = metadata.getSecurity().authenticate(iUserName, iUserPassword); - if (usr != null) - user = new OImmutableUser(security.getVersion(), usr); - else - user = null; - - checkSecurity(ORule.ResourceGeneric.DATABASE, ORole.PERMISSION_READ); - } - } - - // WAKE UP LISTENERS - callOnOpenListeners(); - - failure = false; - } catch (OException e) { - close(); - throw e; - } catch (Exception e) { - close(); - throw OException.wrapException(new ODatabaseException("Cannot open database url=" + getURL()), e); - } finally { - if (failure && ownerProtection) - owner.set(null); - } - return (DB) this; - } - - /** - * Opens a database using an authentication token received as an argument. - * - * @param iToken Authentication token - * @return The Database instance itself giving a "fluent interface". Useful to call multiple methods in chain. - */ - public DB open(final OToken iToken) { - boolean failure = true; - - setupThreadOwner(); - activateOnCurrentThread(); - - try { - if (status == STATUS.OPEN) - throw new IllegalStateException("Database " + getName() + " is already open"); - - if (user != null && !user.getIdentity().equals(iToken.getUserId())) - initialized = false; - - if (storage instanceof OStorageProxy) { - throw new ODatabaseException("Cannot use a token open on remote database"); - } - if (storage.isClosed()) { - // i don't have username and password at this level, anyway the storage embedded don't really need it - OContextConfiguration conf = new OContextConfiguration(); - bindPropertiesToContext(conf, properties); - storage.open("", "", conf); - } - - status = STATUS.OPEN; - - initAtFirstOpen(null, null); - - final OSecurity security = metadata.getSecurity(); - if (user == null || user.getVersion() != security.getVersion()) { - final OUser usr = metadata.getSecurity().authenticate(iToken); - if (usr != null) - user = new OImmutableUser(security.getVersion(), usr); - else - user = null; - - checkSecurity(ORule.ResourceGeneric.DATABASE, ORole.PERMISSION_READ); - } - - // WAKE UP LISTENERS - callOnOpenListeners(); - - failure = false; - } catch (OException e) { - close(); - throw e; - } catch (Exception e) { - close(); - throw OException.wrapException(new ODatabaseException("Cannot open database"), e); - } finally { - if (failure && ownerProtection) { - owner.set(null); - } - - } - return (DB) this; - } - - protected void setupThreadOwner() { - if (!ownerProtection) - return; - - final Thread current = Thread.currentThread(); - final Thread o = owner.get(); - - if (o != null || !owner.compareAndSet(null, current)) { - throw new IllegalStateException("Current instance is owned by other thread" + (o != null ? " : '" + o.getName() + "'" : "")); - } - } - - public void callOnOpenListeners() { - // WAKE UP DB LIFECYCLE LISTENER - for (Iterator it = Orient.instance().getDbLifecycleListeners(); it.hasNext(); ) - it.next().onOpen(getDatabaseOwner()); - - // WAKE UP LISTENERS - for (ODatabaseListener listener : getListenersCopy()) - try { - listener.onOpen(getDatabaseOwner()); - } catch (Throwable t) { - t.printStackTrace(); - } - } - - /** - * {@inheritDoc} - */ - @Override - public DB create() { - return create((Map) null); - } - - /** - * {@inheritDoc} - */ - @Override - public DB create(String incrementalBackupPath) { - create(); - - final OStorage storage = getStorage(); - storage.restoreFromIncrementalBackup(incrementalBackupPath); - loadMetadata(); - - return (DB) this; - } - - protected void loadMetadata() { - metadata = new OMetadataDefault(this); - OSharedContext shared = getStorage().getResource(OSharedContext.class.getName(), new Callable() { - @Override - public OSharedContext call() throws Exception { - OSharedContext shared = new OSharedContext(getStorage()); - return shared; - } - }); - metadata.init(shared); - shared.load(this); - } - - @Override - public DB create(final Map iInitialSettings) { - setupThreadOwner(); - activateOnCurrentThread(); - - try { - if (status == STATUS.OPEN) - throw new IllegalStateException("Database " + getName() + " is already open"); - - if (storage == null) - storage = Orient.instance().loadStorage(url); - OContextConfiguration config = new OContextConfiguration(); - bindPropertiesToContextGlobal(config, iInitialSettings); - bindPropertiesToContext(config, properties); - storage.create(config); - - status = STATUS.OPEN; - - componentsFactory = getStorage().getComponentsFactory(); - - localCache.startup(); - - getStorage().getConfiguration().setRecordSerializer(getSerializer().toString()); - getStorage().getConfiguration().setRecordSerializerVersion(getSerializer().getCurrentVersion()); - - // since 2.1 newly created databases use strict SQL validation by default - getStorage().getConfiguration().setProperty(OStatement.CUSTOM_STRICT_SQL, "true"); - - getStorage().getConfiguration().update(); - - // THIS IF SHOULDN'T BE NEEDED, CREATE HAPPEN ONLY IN EMBEDDED - if (!(getStorage() instanceof OStorageProxy)) - installHooksEmbedded(); - - metadata = new OMetadataDefault(this); - // CREATE THE DEFAULT SCHEMA WITH DEFAULT USER - OSharedContext shared = getStorage().getResource(OSharedContext.class.getName(), new Callable() { - @Override - public OSharedContext call() throws Exception { - OSharedContext shared = new OSharedContext(getStorage()); - return shared; - } - }); - metadata.init(shared); - shared.create(this); - - if (!(getStorage() instanceof OStorageProxy)) - registerHook(new OCommandCacheHook(this), ORecordHook.HOOK_POSITION.REGULAR); - - registerHook(new OSecurityTrackerHook(metadata.getSecurity(), this), ORecordHook.HOOK_POSITION.LAST); - - final OUser usr = getMetadata().getSecurity().getUser(OUser.ADMIN); - - if (usr == null) - user = null; - else - user = new OImmutableUser(getMetadata().getSecurity().getVersion(), usr); - - // WAKE UP DB LIFECYCLE LISTENER - for (Iterator it = Orient.instance().getDbLifecycleListeners(); it.hasNext(); ) - it.next().onCreate(getDatabaseOwner()); - - // WAKE UP LISTENERS - for (ODatabaseListener listener : browseListeners()) - try { - listener.onCreate(this); - } catch (Throwable ignore) { - } - } catch (Exception e) { - // REMOVE THE (PARTIAL) DATABASE - try { - drop(); - } catch (Exception ex) { - // IGNORE IT - } - - // DELETE THE STORAGE TOO - try { - if (storage == null) - storage = Orient.instance().loadStorage(url); - storage.delete(); - } catch (Exception ex) { - // IGNORE IT - } - - status = STATUS.CLOSED; - owner.set(null); - - throw OException.wrapException(new ODatabaseException("Cannot create database '" + getName() + "'"), e); - } - return (DB) this; - } - - /** - * {@inheritDoc} - */ - @Override - public void drop() { - checkOpeness(); - checkIfActive(); - - checkSecurity(ORule.ResourceGeneric.DATABASE, ORole.PERMISSION_DELETE); - - callOnDropListeners(); - - if (metadata != null) { - metadata.close(); - metadata = null; - } - - closeOnDelete(); - - try { - if (storage == null) - storage = Orient.instance().loadStorage(url); - - storage.delete(); - storage = null; - sharedContext = null; - status = STATUS.CLOSED; - ODatabaseRecordThreadLocal.INSTANCE.remove(); - clearOwner(); - - } catch (OException e) { - // PASS THROUGH - throw e; - } catch (Exception e) { - throw OException.wrapException(new ODatabaseException("Cannot delete database"), e); - } - } - - /** - * Returns a copy of current database if it's open. The returned instance can be used by another thread without affecting current - * instance. The database copy is not set in thread local. - */ - public ODatabaseDocumentInternal copy() { - ODatabaseDocumentInternal dbInThreadLocal = ODatabaseRecordThreadLocal.INSTANCE.getIfDefined(); - if (this.isClosed()) - throw new ODatabaseException("Cannot copy a closed db"); - - final ODatabaseDocumentTx db = new ODatabaseDocumentTx(this.url); - db.setupThreadOwner(); - - db.user = this.user; - db.properties.putAll(this.properties); - db.serializer = this.serializer; - - db.componentsFactory = this.componentsFactory; - - db.initialized = true; - if (storage instanceof OStorageProxy) { - db.storage = ((OStorageProxy) storage).copy(this, db); - ((OStorageProxy) db.storage).addUser(); - } else { - db.storage = storage; - } - - db.setStatus(STATUS.OPEN); - db.loadMetadata(); - - if (!(db.getStorage() instanceof OStorageProxy)) - db.installHooksEmbedded(); - else - db.installHooksRemote(); - - db.initialized = true; - - if (dbInThreadLocal != null) { - dbInThreadLocal.activateOnCurrentThread(); - } else { - if (ODatabaseRecordThreadLocal.INSTANCE.isDefined()) { - ODatabaseRecordThreadLocal.INSTANCE.remove(); - } - } - - return db; - } - - public void callOnCloseListeners() { - // WAKE UP DB LIFECYCLE LISTENER - for (Iterator it = Orient.instance().getDbLifecycleListeners(); it.hasNext(); ) - it.next().onClose(getDatabaseOwner()); - - // WAKE UP LISTENERS - for (ODatabaseListener listener : getListenersCopy()) - try { - listener.onClose(getDatabaseOwner()); - } catch (Throwable t) { - t.printStackTrace(); - } - } - - public void callOnDropListeners() { - // WAKE UP DB LIFECYCLE LISTENER - for (Iterator it = Orient.instance().getDbLifecycleListeners(); it.hasNext(); ) { - activateOnCurrentThread(); - it.next().onDrop(getDatabaseOwner()); - } - - // WAKE UP LISTENERS - for (ODatabaseListener listener : getListenersCopy()) - try { - activateOnCurrentThread(); - listener.onDelete(getDatabaseOwner()); - } catch (Throwable t) { - t.printStackTrace(); - } - } - - /** - * {@inheritDoc} - */ - public RET getRecord(final OIdentifiable iIdentifiable) { - if (iIdentifiable instanceof ORecord) - return (RET) iIdentifiable; - return (RET) load(iIdentifiable.getIdentity()); - } - - @Override - public void reload() { - checkIfActive(); - - if (this.isClosed()) - throw new ODatabaseException("Cannot reload a closed db"); - - metadata.reload(); - storage.reload(); - } - - /** - * {@inheritDoc} - */ - public RET load(final ORID iRecordId, final String iFetchPlan, final boolean iIgnoreCache) { - return (RET) executeReadRecord((ORecordId) iRecordId, null, -1, iFetchPlan, iIgnoreCache, !iIgnoreCache, false, - OStorage.LOCKING_STRATEGY.DEFAULT, new SimpleRecordReader(prefetchRecords)); - } - - /** - * Deletes the record checking the version. - */ - public ODatabase delete(final ORID iRecord, final int iVersion) { - executeDeleteRecord(iRecord, iVersion, true, OPERATION_MODE.SYNCHRONOUS, false); - return this; - } - - public ODatabase cleanOutRecord(final ORID iRecord, final int iVersion) { - executeDeleteRecord(iRecord, iVersion, true, OPERATION_MODE.SYNCHRONOUS, true); - return this; - } - - public String getType() { - return TYPE; - } - - /** - * Deletes the record without checking the version. - */ - public ODatabaseDocument delete(final ORID iRecord, final OPERATION_MODE iMode) { - ORecord record = iRecord.getRecord(); - if (record == null) - return this; - - delete(record, iMode); - return this; - } - - public ODatabaseDocument delete(final ORecord iRecord, final OPERATION_MODE iMode) { - checkIfActive(); - currentTx.deleteRecord(iRecord, iMode); - return this; - } - - public ORecordIteratorCluster browseCluster(final String iClusterName, final Class iClass) { - checkSecurity(ORule.ResourceGeneric.CLUSTER, ORole.PERMISSION_READ, iClusterName); - - checkIfActive(); - - final int clusterId = getClusterIdByName(iClusterName); - - return new ORecordIteratorCluster(this, this, clusterId); - } - - /** - * {@inheritDoc} - */ - @Override - @Deprecated - public ORecordIteratorCluster browseCluster(final String iClusterName, final Class iRecordClass, - final long startClusterPosition, final long endClusterPosition, final boolean loadTombstones) { - checkSecurity(ORule.ResourceGeneric.CLUSTER, ORole.PERMISSION_READ, iClusterName); - checkIfActive(); - - final int clusterId = getClusterIdByName(iClusterName); - - return new ORecordIteratorCluster(this, this, clusterId, startClusterPosition, endClusterPosition, loadTombstones, - OStorage.LOCKING_STRATEGY.DEFAULT); - } - - @Override - public ORecordIteratorCluster browseCluster(String iClusterName, Class iRecordClass, - long startClusterPosition, long endClusterPosition) { - checkSecurity(ORule.ResourceGeneric.CLUSTER, ORole.PERMISSION_READ, iClusterName); - checkIfActive(); - - final int clusterId = getClusterIdByName(iClusterName); - - return new ORecordIteratorCluster(this, this, clusterId, startClusterPosition, endClusterPosition); - } - - /** - * {@inheritDoc} - */ - public OCommandRequest command(final OCommandRequest iCommand) { - checkSecurity(ORule.ResourceGeneric.COMMAND, ORole.PERMISSION_READ); - checkIfActive(); - - final OCommandRequestInternal command = (OCommandRequestInternal) iCommand; - - try { - command.reset(); - return command; - - } catch (Exception e) { - throw OException.wrapException(new ODatabaseException("Error on command execution"), e); - } - } - - /** - * {@inheritDoc} - */ - public > RET query(final OQuery iCommand, final Object... iArgs) { - checkIfActive(); - iCommand.reset(); - return (RET) iCommand.execute(iArgs); - } - - /** - * {@inheritDoc} - */ - public byte getRecordType() { - return recordType; - } - - /** - * {@inheritDoc} - */ - @Override - public long countClusterElements(final int[] iClusterIds) { - return countClusterElements(iClusterIds, false); - } - - /** - * {@inheritDoc} - */ - @Override - public long countClusterElements(final int iClusterId) { - return countClusterElements(iClusterId, false); - } - - /** - * {@inheritDoc} - */ - @Override - public void truncateCluster(String clusterName) { - command(new OCommandSQL("truncate cluster " + clusterName)).execute(); - } - - /** - * {@inheritDoc} - */ - @Override - public long countClusterElements(int iClusterId, boolean countTombstones) { - final String name = getClusterNameById(iClusterId); - if (name == null) - return 0; - - checkSecurity(ORule.ResourceGeneric.CLUSTER, ORole.PERMISSION_READ, name); - checkIfActive(); - - return storage.count(iClusterId, countTombstones); - } - - /** - * {@inheritDoc} - */ - @Override - public long countClusterElements(int[] iClusterIds, boolean countTombstones) { - checkIfActive(); - String name; - for (int iClusterId : iClusterIds) { - name = getClusterNameById(iClusterId); - checkSecurity(ORule.ResourceGeneric.CLUSTER, ORole.PERMISSION_READ, name); - } - - return storage.count(iClusterIds, countTombstones); - } - - /** - * {@inheritDoc} - */ - @Override - public long countClusterElements(final String iClusterName) { - checkSecurity(ORule.ResourceGeneric.CLUSTER, ORole.PERMISSION_READ, iClusterName); - checkIfActive(); - - final int clusterId = getClusterIdByName(iClusterName); - if (clusterId < 0) - throw new IllegalArgumentException("Cluster '" + iClusterName + "' was not found"); - return storage.count(clusterId); - } - - /** - * {@inheritDoc} - */ - public OMetadataDefault getMetadata() { - checkOpeness(); - return metadata; - } - - /** - * {@inheritDoc} - */ - public DB checkSecurity(final ORule.ResourceGeneric resourceGeneric, final String resourceSpecific, - final int iOperation) { - if (user != null) { - try { - user.allow(resourceGeneric, resourceSpecific, iOperation); - } catch (OSecurityAccessException e) { - - if (OLogManager.instance().isDebugEnabled()) - OLogManager.instance() - .debug(this, "User '%s' tried to access the reserved resource '%s.%s', operation '%s'", getUser(), resourceGeneric, - resourceSpecific, iOperation); - - throw e; - } - } - return (DB) this; - } - - /** - * {@inheritDoc} - */ - public DB checkSecurity(final ORule.ResourceGeneric iResourceGeneric, final int iOperation, - final Object... iResourcesSpecific) { - - if (user != null) { - try { - if (iResourcesSpecific.length != 0) { - for (Object target : iResourcesSpecific) { - if (target != null) { - user.allow(iResourceGeneric, target.toString(), iOperation); - } else - user.allow(iResourceGeneric, null, iOperation); - } - } else - user.allow(iResourceGeneric, null, iOperation); - } catch (OSecurityAccessException e) { - if (OLogManager.instance().isDebugEnabled()) - OLogManager.instance() - .debug(this, "[checkSecurity] User '%s' tried to access the reserved resource '%s', target(s) '%s', operation '%s'", - getUser(), iResourceGeneric, Arrays.toString(iResourcesSpecific), iOperation); - - throw e; - } - } - return (DB) this; - } - - /** - * {@inheritDoc} - */ - public DB checkSecurity(final ORule.ResourceGeneric iResourceGeneric, final int iOperation, - final Object iResourceSpecific) { - checkOpeness(); - if (user != null) { - try { - if (iResourceSpecific != null) - user.allow(iResourceGeneric, iResourceSpecific.toString(), iOperation); - else - user.allow(iResourceGeneric, null, iOperation); - } catch (OSecurityAccessException e) { - if (OLogManager.instance().isDebugEnabled()) - OLogManager.instance() - .debug(this, "[checkSecurity] User '%s' tried to access the reserved resource '%s', target '%s', operation '%s'", - getUser(), iResourceGeneric, iResourceSpecific, iOperation); - - throw e; - } - } - return (DB) this; - } - - /** - * {@inheritDoc} - */ - @Override - public ODatabaseInternal getDatabaseOwner() { - ODatabaseInternal current = databaseOwner; - - while (current != null && current != this && current.getDatabaseOwner() != current) - current = current.getDatabaseOwner(); - - return current; - } - - /** - * {@inheritDoc} - */ - @Override - public ODatabaseInternal setDatabaseOwner(ODatabaseInternal iOwner) { - databaseOwner = iOwner; - return this; - } - - /** - * {@inheritDoc} - */ - public boolean isRetainRecords() { - return retainRecords; - } - - /** - * {@inheritDoc} - */ - public ODatabaseDocument setRetainRecords(boolean retainRecords) { - this.retainRecords = retainRecords; - return this; - } - - /** - * {@inheritDoc} - */ - public DB setStatus(final STATUS status) { - checkIfActive(); - setStatusInternal(status); - return (DB) this; - } - - public void setStatusInternal(final STATUS status) { - this.status = status; - } - - /** - * Deprecated since v2.2 - */ - @Deprecated - public void setDefaultClusterIdInternal(final int iDefClusterId) { - checkIfActive(); - getStorage().setDefaultClusterId(iDefClusterId); - } - - /** - * {@inheritDoc} - */ - public void setInternal(final ATTRIBUTES iAttribute, final Object iValue) { - set(iAttribute, iValue); - } - - /** - * {@inheritDoc} - */ - public OSecurityUser getUser() { - return user; - } - - /** - * {@inheritDoc} - */ - public void setUser(final OSecurityUser user) { - checkIfActive(); - if (user instanceof OUser) { - OMetadata metadata = getMetadata(); - if (metadata != null) { - final OSecurity security = metadata.getSecurity(); - this.user = new OImmutableUser(security.getVersion(), (OUser) user); - } else - this.user = new OImmutableUser(-1, (OUser) user); - } else - this.user = (OImmutableUser) user; - } - - public void reloadUser() { - if (user != null) { - activateOnCurrentThread(); - - OMetadata metadata = getMetadata(); - - if (metadata != null) { - final OSecurity security = metadata.getSecurity(); - OUser secGetUser = security.getUser(user.getName()); - - if (secGetUser != null) - user = new OImmutableUser(security.getVersion(), secGetUser); - else - user = new OImmutableUser(-1, new OUser()); - } else - user = new OImmutableUser(-1, new OUser()); - } - } - - /** - * {@inheritDoc} - */ - public boolean isMVCC() { - return mvcc; - } - - /** - * {@inheritDoc} - */ - public > DB setMVCC(boolean mvcc) { - this.mvcc = mvcc; - return (DB) this; - } - - /** - * {@inheritDoc} - */ - public ODictionary getDictionary() { - checkOpeness(); - return metadata.getIndexManager().getDictionary(); - } - - /** - * {@inheritDoc} - */ - public > DB registerHook(final ORecordHook iHookImpl, final ORecordHook.HOOK_POSITION iPosition) { - checkOpeness(); - checkIfActive(); - - final Map tmp = new LinkedHashMap(hooks); - tmp.put(iHookImpl, iPosition); - hooks.clear(); - for (ORecordHook.HOOK_POSITION p : ORecordHook.HOOK_POSITION.values()) { - for (Map.Entry e : tmp.entrySet()) { - if (e.getValue() == p) - hooks.put(e.getKey(), e.getValue()); - } - } - - compileHooks(); - - return (DB) this; - } - - /** - * {@inheritDoc} - */ - public > DB registerHook(final ORecordHook iHookImpl) { - return (DB) registerHook(iHookImpl, ORecordHook.HOOK_POSITION.REGULAR); - } - - /** - * {@inheritDoc} - */ - public > DB unregisterHook(final ORecordHook iHookImpl) { - checkIfActive(); - if (iHookImpl != null) { - iHookImpl.onUnregister(); - hooks.remove(iHookImpl); - compileHooks(); - } - - return (DB) this; - } - - /** - * {@inheritDoc} - */ - @Override - public OLocalRecordCache getLocalCache() { - return localCache; - } - - /** - * {@inheritDoc} - */ - public Map getHooks() { - return unmodifiableHooks; - } - - /** - * Callback the registered hooks if any. - * - * @param type Hook type. Define when hook is called. - * @param id Record received in the callback - * @return True if the input record is changed, otherwise false - */ - public ORecordHook.RESULT callbackHooks(final ORecordHook.TYPE type, final OIdentifiable id) { - if (id == null || hooks.isEmpty() || id.getIdentity().getClusterId() == 0) - return ORecordHook.RESULT.RECORD_NOT_CHANGED; - - final ORecordHook.SCOPE scope = ORecordHook.SCOPE.typeToScope(type); - final int scopeOrdinal = scope.ordinal(); - - final ORID identity = id.getIdentity().copy(); - if (!pushInHook(identity)) - return ORecordHook.RESULT.RECORD_NOT_CHANGED; - - try { - final ORecord rec = id.getRecord(); - if (rec == null) - return ORecordHook.RESULT.RECORD_NOT_CHANGED; - - final OScenarioThreadLocal.RUN_MODE runMode = OScenarioThreadLocal.INSTANCE.getRunMode(); - - boolean recordChanged = false; - for (ORecordHook hook : hooksByScope[scopeOrdinal]) { - switch (runMode) { - case DEFAULT: // NON_DISTRIBUTED OR PROXIED DB - if (getStorage().isDistributed() - && hook.getDistributedExecutionMode() == ORecordHook.DISTRIBUTED_EXECUTION_MODE.TARGET_NODE) - // SKIP - continue; - break; // TARGET NODE - case RUNNING_DISTRIBUTED: - if (hook.getDistributedExecutionMode() == ORecordHook.DISTRIBUTED_EXECUTION_MODE.SOURCE_NODE) - continue; - } - - final ORecordHook.RESULT res = hook.onTrigger(type, rec); - - if (res == ORecordHook.RESULT.RECORD_CHANGED) - recordChanged = true; - else if (res == ORecordHook.RESULT.SKIP_IO) - // SKIP IO OPERATION - return res; - else if (res == ORecordHook.RESULT.SKIP) - // SKIP NEXT HOOKS AND RETURN IT - return res; - else if (res == ORecordHook.RESULT.RECORD_REPLACED) - return res; - } - - return recordChanged ? ORecordHook.RESULT.RECORD_CHANGED : ORecordHook.RESULT.RECORD_NOT_CHANGED; - - } finally { - popInHook(identity); - } - } - - /** - * {@inheritDoc} - */ - public boolean isValidationEnabled() { - return (Boolean) get(ATTRIBUTES.VALIDATION); - } - - /** - * {@inheritDoc} - */ - public DB setValidationEnabled(final boolean iEnabled) { - set(ATTRIBUTES.VALIDATION, iEnabled); - return (DB) this; - } - - public ORecordConflictStrategy getConflictStrategy() { - checkIfActive(); - return getStorage().getConflictStrategy(); - } - - public ODatabaseDocumentTx setConflictStrategy(final String iStrategyName) { - checkIfActive(); - getStorage().setConflictStrategy(Orient.instance().getRecordConflictStrategy().getStrategy(iStrategyName)); - return this; - } - - public ODatabaseDocumentTx setConflictStrategy(final ORecordConflictStrategy iResolver) { - checkIfActive(); - getStorage().setConflictStrategy(iResolver); - return this; - } - - @Override - public OContextConfiguration getConfiguration() { - checkIfActive(); - if (storage != null) - return storage.getConfiguration().getContextConfiguration(); - return null; - } - - @Override - public boolean declareIntent(final OIntent iIntent) { - checkIfActive(); - - if (currentIntent != null) { - if (iIntent != null && iIntent.getClass().equals(currentIntent.getClass())) - // SAME INTENT: JUMP IT - return false; - - // END CURRENT INTENT - currentIntent.end(this); - } - - currentIntent = iIntent; - - if (iIntent != null) - iIntent.begin(this); - - return true; - } - - @Override - public boolean exists() { - if (status == STATUS.OPEN) - return true; - - if (storage == null) - storage = Orient.instance().loadStorage(url); - - return storage.exists(); - } - - @Override - public void close() { - checkIfActive(); - - try { - localCache.shutdown(); - - if (isClosed()) { - status = STATUS.CLOSED; - return; - } - - try { - commit(true); - } catch (Exception e) { - OLogManager.instance().error(this, "Exception during commit of active transaction", e); - } - - if (status != STATUS.OPEN) - return; - - callOnCloseListeners(); - - if (currentIntent != null) { - currentIntent.end(this); - currentIntent = null; - } - sharedContext = null; - status = STATUS.CLOSED; - - localCache.clear(); - - if (!keepStorageOpen && storage != null) - storage.close(); - - } finally { - // ALWAYS RESET TL - ODatabaseRecordThreadLocal.INSTANCE.remove(); - clearOwner(); - } - } - - protected void clearOwner() { - if (!ownerProtection) - return; - owner.set(null); - } - - @Override - public STATUS getStatus() { - return status; - } - - @Override - public long getSize() { - checkIfActive(); - return storage.getSize(); - } - - @Override - public String getName() { - return storage != null ? storage.getName() : url; - } - - @Override - public String getURL() { - return url != null ? url : storage.getURL(); - } - - @Override - public int getDefaultClusterId() { - checkIfActive(); - return storage.getDefaultClusterId(); - } - - @Override - public int getClusters() { - checkIfActive(); - return storage.getClusters(); - } - - @Override - public boolean existsCluster(final String iClusterName) { - checkIfActive(); - return storage.getClusterNames().contains(iClusterName.toLowerCase()); - } - - @Override - public Collection getClusterNames() { - checkIfActive(); - return storage.getClusterNames(); - } - - @Override - public int getClusterIdByName(final String iClusterName) { - if (iClusterName == null) - return -1; - - checkIfActive(); - return storage.getClusterIdByName(iClusterName.toLowerCase()); - } - - @Override - public String getClusterNameById(final int iClusterId) { - if (iClusterId < 0) - return null; - - checkIfActive(); - return storage.getPhysicalClusterNameById(iClusterId); - } - - @Override - public long getClusterRecordSizeByName(final String clusterName) { - checkIfActive(); - try { - return storage.getClusterById(getClusterIdByName(clusterName)).getRecordsSize(); - } catch (Exception e) { - throw OException.wrapException(new ODatabaseException("Error on reading records size for cluster '" + clusterName + "'"), e); - } - } - - @Override - public long getClusterRecordSizeById(final int clusterId) { - checkIfActive(); - try { - return storage.getClusterById(clusterId).getRecordsSize(); - } catch (Exception e) { - throw OException - .wrapException(new ODatabaseException("Error on reading records size for cluster with id '" + clusterId + "'"), e); - } - } - - @Override - public boolean isClosed() { - return status == STATUS.CLOSED || storage.isClosed(); - } - - @Override - public int addCluster(final String iClusterName, final Object... iParameters) { - checkIfActive(); - return storage.addCluster(iClusterName, false, iParameters); - } - - @Override - public int addCluster(final String iClusterName, final int iRequestedId, final Object... iParameters) { - checkIfActive(); - return storage.addCluster(iClusterName, iRequestedId, false, iParameters); - } - - @Override - public boolean dropCluster(final String iClusterName, final boolean iTruncate) { - checkIfActive(); - final int clusterId = getClusterIdByName(iClusterName); - OSchemaProxy schema = metadata.getSchema(); - OClass clazz = schema.getClassByClusterId(clusterId); - if (clazz != null) - clazz.removeClusterId(clusterId); - if (schema.getBlobClusters().contains(clusterId)) - schema.removeBlobCluster(iClusterName); - getLocalCache().freeCluster(clusterId); - return storage.dropCluster(iClusterName, iTruncate); - } - - @Override - public boolean dropCluster(final int iClusterId, final boolean iTruncate) { - checkIfActive(); - - checkSecurity(ORule.ResourceGeneric.CLUSTER, ORole.PERMISSION_DELETE, getClusterNameById(iClusterId)); - - OSchemaProxy schema = metadata.getSchema(); - final OClass clazz = schema.getClassByClusterId(iClusterId); - if (clazz != null) - clazz.removeClusterId(iClusterId); - getLocalCache().freeCluster(iClusterId); - if (schema.getBlobClusters().contains(iClusterId)) - schema.removeBlobCluster(getClusterNameById(iClusterId)); - - return storage.dropCluster(iClusterId, iTruncate); - } - - @Override - public Object setProperty(final String iName, final Object iValue) { - if (iValue == null) - return properties.remove(iName.toLowerCase()); - else - return properties.put(iName.toLowerCase(), iValue); - } - - @Override - public Object getProperty(final String iName) { - return properties.get(iName.toLowerCase()); - } - - @Override - public Iterator> getProperties() { - return properties.entrySet().iterator(); - } - - @Override - public Object get(final ATTRIBUTES iAttribute) { - checkIfActive(); - - if (iAttribute == null) - throw new IllegalArgumentException("attribute is null"); - - switch (iAttribute) { - case STATUS: - return getStatus(); - case DEFAULTCLUSTERID: - return getDefaultClusterId(); - case TYPE: - return getMetadata().getImmutableSchemaSnapshot().existsClass("V") ? "graph" : "document"; - case DATEFORMAT: - return storage.getConfiguration().dateFormat; - - case DATETIMEFORMAT: - return storage.getConfiguration().dateTimeFormat; - - case TIMEZONE: - return storage.getConfiguration().getTimeZone().getID(); - - case LOCALECOUNTRY: - return storage.getConfiguration().getLocaleCountry(); - - case LOCALELANGUAGE: - return storage.getConfiguration().getLocaleLanguage(); - - case CHARSET: - return storage.getConfiguration().getCharset(); - - case CUSTOM: - return storage.getConfiguration().getProperties(); - - case CLUSTERSELECTION: - return storage.getConfiguration().getClusterSelection(); - - case MINIMUMCLUSTERS: - return storage.getConfiguration().getMinimumClusters(); - - case CONFLICTSTRATEGY: - return storage.getConfiguration().getConflictStrategy(); - - case VALIDATION: - return storage.getConfiguration().isValidationEnabled(); - } - - return null; - } - - @Override - public DB set(final ATTRIBUTES iAttribute, final Object iValue) { - checkIfActive(); - - if (iAttribute == null) - throw new IllegalArgumentException("attribute is null"); - - final String stringValue = OIOUtils.getStringContent(iValue != null ? iValue.toString() : null); - - switch (iAttribute) { - case STATUS: - if (stringValue == null) - throw new IllegalArgumentException("DB status can't be null"); - setStatus(STATUS.valueOf(stringValue.toUpperCase(Locale.ENGLISH))); - break; - - case DEFAULTCLUSTERID: - if (iValue != null) { - if (iValue instanceof Number) - storage.setDefaultClusterId(((Number) iValue).intValue()); - else - storage.setDefaultClusterId(storage.getClusterIdByName(iValue.toString())); - } - break; - - case TYPE: - throw new IllegalArgumentException("Database type cannot be changed at run-time"); - - case DATEFORMAT: - if (stringValue == null) - throw new IllegalArgumentException("date format is null"); - - // CHECK FORMAT - new SimpleDateFormat(stringValue).format(new Date()); - - storage.getConfiguration().dateFormat = stringValue; - storage.getConfiguration().update(); - break; - - case DATETIMEFORMAT: - if (stringValue == null) - throw new IllegalArgumentException("date format is null"); - - // CHECK FORMAT - new SimpleDateFormat(stringValue).format(new Date()); - - storage.getConfiguration().dateTimeFormat = stringValue; - storage.getConfiguration().update(); - break; - - case TIMEZONE: - if (stringValue == null) - throw new IllegalArgumentException("Timezone can't be null"); - - // for backward compatibility, until 2.1.13 OrientDB accepted timezones in lowercase as well - TimeZone timeZoneValue = TimeZone.getTimeZone(stringValue.toUpperCase()); - if (timeZoneValue.equals(TimeZone.getTimeZone("GMT"))) { - timeZoneValue = TimeZone.getTimeZone(stringValue); - } - - storage.getConfiguration().setTimeZone(timeZoneValue); - storage.getConfiguration().update(); - break; - - case LOCALECOUNTRY: - storage.getConfiguration().setLocaleCountry(stringValue); - storage.getConfiguration().update(); - break; - - case LOCALELANGUAGE: - storage.getConfiguration().setLocaleLanguage(stringValue); - storage.getConfiguration().update(); - break; - - case CHARSET: - storage.getConfiguration().setCharset(stringValue); - storage.getConfiguration().update(); - break; - - case CUSTOM: - int indx = stringValue != null ? stringValue.indexOf('=') : -1; - if (indx < 0) { - if ("clear".equalsIgnoreCase(stringValue)) { - clearCustomInternal(); - } else - throw new IllegalArgumentException("Syntax error: expected = or clear, instead found: " + iValue); - } else { - String customName = stringValue.substring(0, indx).trim(); - String customValue = stringValue.substring(indx + 1).trim(); - if (customValue.isEmpty()) - removeCustomInternal(customName); - else - setCustomInternal(customName, customValue); - } - break; - - case CLUSTERSELECTION: - storage.getConfiguration().setClusterSelection(stringValue); - storage.getConfiguration().update(); - break; - - case MINIMUMCLUSTERS: - if (iValue != null) { - if (iValue instanceof Number) - storage.getConfiguration().setMinimumClusters(((Number) iValue).intValue()); - else - storage.getConfiguration().setMinimumClusters(Integer.parseInt(stringValue)); - } else - // DEFAULT = 1 - storage.getConfiguration().setMinimumClusters(1); - - storage.getConfiguration().update(); - break; - - case CONFLICTSTRATEGY: - storage.setConflictStrategy(Orient.instance().getRecordConflictStrategy().getStrategy(stringValue)); - storage.getConfiguration().setConflictStrategy(stringValue); - storage.getConfiguration().update(); - break; - - case VALIDATION: - storage.getConfiguration().setValidation(Boolean.parseBoolean(stringValue)); - storage.getConfiguration().update(); - break; - - default: - throw new IllegalArgumentException("Option '" + iAttribute + "' not supported on alter database"); - - } - - return (DB) this; - } - - public DB setCustom(final String name, final Object iValue) { - checkIfActive(); - - if ("clear".equalsIgnoreCase(name) && iValue == null) { - clearCustomInternal(); - } else { - String customName = name; - String customValue = iValue == null ? null : "" + iValue; - if (customName == null || customValue.isEmpty()) - removeCustomInternal(customName); - else - setCustomInternal(customName, customValue); - } - - return (DB) this; - } - - @Override - public ORecordMetadata getRecordMetadata(final ORID rid) { - checkIfActive(); - return storage.getRecordMetadata(rid); - } - - public OTransaction getTransaction() { - checkIfActive(); - return currentTx; - } - - @SuppressWarnings("unchecked") - @Override - public RET load(final ORecord iRecord, final String iFetchPlan) { - checkIfActive(); - return (RET) currentTx.loadRecord(iRecord.getIdentity(), iRecord, iFetchPlan, false, false, OStorage.LOCKING_STRATEGY.DEFAULT); - } - - @SuppressWarnings("unchecked") - @Override - @Deprecated - public RET load(ORecord iRecord, String iFetchPlan, boolean iIgnoreCache, boolean loadTombstone, - OStorage.LOCKING_STRATEGY iLockingStrategy) { - checkIfActive(); - return (RET) currentTx - .loadRecord(iRecord.getIdentity(), iRecord, iFetchPlan, iIgnoreCache, !iIgnoreCache, loadTombstone, iLockingStrategy); - } - - @SuppressWarnings("unchecked") - @Override - @Deprecated - public RET load(final ORecord iRecord, final String iFetchPlan, final boolean iIgnoreCache, - final boolean iUpdateCache, final boolean loadTombstone, final OStorage.LOCKING_STRATEGY iLockingStrategy) { - checkIfActive(); - return (RET) currentTx - .loadRecord(iRecord.getIdentity(), iRecord, iFetchPlan, iIgnoreCache, iUpdateCache, loadTombstone, iLockingStrategy); - } - - @SuppressWarnings("unchecked") - @Override - public RET load(final ORecord iRecord) { - checkIfActive(); - return (RET) currentTx.loadRecord(iRecord.getIdentity(), iRecord, null, false); - } - - @SuppressWarnings("unchecked") - @Override - public RET load(final ORID recordId) { - return (RET) currentTx.loadRecord(recordId, null, null, false); - } - - @SuppressWarnings("unchecked") - @Override - public RET load(final ORID iRecordId, final String iFetchPlan) { - checkIfActive(); - return (RET) currentTx.loadRecord(iRecordId, null, iFetchPlan, false); - } - - @SuppressWarnings("unchecked") - public RET loadIfVersionIsNotLatest(final ORID rid, final int recordVersion, String fetchPlan, - boolean ignoreCache) throws ORecordNotFoundException { - checkIfActive(); - return (RET) currentTx.loadRecordIfVersionIsNotLatest(rid, recordVersion, fetchPlan, ignoreCache); - } - - @SuppressWarnings("unchecked") - @Override - @Deprecated - public RET load(final ORID iRecordId, String iFetchPlan, final boolean iIgnoreCache, - final boolean loadTombstone, OStorage.LOCKING_STRATEGY iLockingStrategy) { - checkIfActive(); - return (RET) currentTx.loadRecord(iRecordId, null, iFetchPlan, iIgnoreCache, loadTombstone, iLockingStrategy); - } - - @SuppressWarnings("unchecked") - @Override - @Deprecated - public RET load(final ORID iRecordId, String iFetchPlan, final boolean iIgnoreCache, - final boolean iUpdateCache, final boolean loadTombstone, OStorage.LOCKING_STRATEGY iLockingStrategy) { - checkIfActive(); - return (RET) currentTx.loadRecord(iRecordId, null, iFetchPlan, iIgnoreCache, iUpdateCache, loadTombstone, iLockingStrategy); - } - - @SuppressWarnings("unchecked") - public RET reload(final ORecord iRecord) { - return reload(iRecord, null, false); - } - - @SuppressWarnings("unchecked") - public RET reload(final ORecord iRecord, final String iFetchPlan) { - return reload(iRecord, iFetchPlan, false); - } - - @SuppressWarnings("unchecked") - @Override - public RET reload(final ORecord iRecord, final String iFetchPlan, final boolean iIgnoreCache) { - return reload(iRecord, iFetchPlan, iIgnoreCache, true); - } - - @Override - public RET reload(ORecord record, String fetchPlan, boolean ignoreCache, boolean force) { - checkIfActive(); - - final ORecord loadedRecord = currentTx.reloadRecord(record.getIdentity(), record, fetchPlan, ignoreCache, force); - - if (loadedRecord != null && record != loadedRecord) { - record.fromStream(loadedRecord.toStream()); - ORecordInternal.setVersion(record, loadedRecord.getVersion()); - } else if (loadedRecord == null) { - throw new ORecordNotFoundException(record.getIdentity()); - } - - return (RET) record; - } - - /** - * Deletes the record without checking the version. - */ - public ODatabaseDocument delete(final ORID iRecord) { - checkOpeness(); - checkIfActive(); - - final ORecord rec = iRecord.getRecord(); - if (rec != null) - rec.delete(); - return this; - } - - @Override - public boolean hide(ORID rid) { - checkOpeness(); - checkIfActive(); - - if (currentTx.isActive()) - throw new ODatabaseException("This operation can be executed only in non transaction mode"); - - return executeHideRecord(rid, OPERATION_MODE.SYNCHRONOUS); - } - - @Override - public OBinarySerializerFactory getSerializerFactory() { - return componentsFactory.binarySerializerFactory; - } - - public ODatabaseDocument begin(final OTransaction iTx) { - checkOpeness(); - checkIfActive(); - - if (currentTx.isActive() && iTx.equals(currentTx)) { - currentTx.begin(); - return this; - } - - currentTx.rollback(true, 0); - - // WAKE UP LISTENERS - for (ODatabaseListener listener : browseListeners()) - try { - listener.onBeforeTxBegin(this); - } catch (Exception e) { - final String message = "Error before the transaction begin"; - - OLogManager.instance().error(this, message, e); - throw OException.wrapException(new OTransactionBlockedException(message), e); - } - - currentTx = iTx; - currentTx.begin(); - - return this; - } - - /** - * {@inheritDoc} - */ - public RET load(final ORecord iRecord, final String iFetchPlan, final boolean iIgnoreCache) { - return (RET) executeReadRecord((ORecordId) iRecord.getIdentity(), iRecord, -1, iFetchPlan, iIgnoreCache, !iIgnoreCache, false, - OStorage.LOCKING_STRATEGY.NONE, new SimpleRecordReader(prefetchRecords)); - } - - /** - * This method is internal, it can be subject to signature change or be removed, do not use. - * - * @Internal - */ - public Set executeReadRecords(final Set iRids, final boolean ignoreCache) { - checkOpeness(); - checkIfActive(); - - getMetadata().makeThreadLocalSchemaSnapshot(); - ORecordSerializationContext.pushContext(); - try { - - final Set records = new HashSet(iRids.size() > 0 ? iRids.size() : 1); - - if (iRids.isEmpty()) - return records; - - final Collection rids = new ArrayList(iRids); - - for (Iterator it = rids.iterator(); it.hasNext(); ) { - final ORecordId rid = it.next(); - - // SEARCH IN LOCAL TX - ORecord record = getTransaction().getRecord(rid); - if (record == OTransactionRealAbstract.DELETED_RECORD) { - // DELETED IN TX - it.remove(); - continue; - } - - if (record == null && !ignoreCache) - // SEARCH INTO THE CACHE - record = getLocalCache().findRecord(rid); - - if (record != null) { - // FOUND FROM CACHE - records.add(record); - it.remove(); - } - } - - final Collection> rawRecords = ((OAbstractPaginatedStorage) storage.getUnderlying()) - .readRecords(rids); - for (OPair entry : rawRecords) { - // NO SAME RECORD TYPE: CAN'T REUSE OLD ONE BUT CREATE A NEW ONE FOR IT - final ORecord record = Orient.instance().getRecordFactoryManager().newInstance(entry.value.recordType); - ORecordInternal.fill(record, entry.key, entry.value.version, entry.value.buffer, false); - records.add(record); - } - - return records; - - } finally { - ORecordSerializationContext.pullContext(); - getMetadata().clearThreadLocalSchemaSnapshot(); - } - } - - @Override - public void setPrefetchRecords(boolean prefetchRecords) { - this.prefetchRecords = prefetchRecords; - } - - @Override - public boolean isPrefetchRecords() { - return prefetchRecords; - } - - /** - * This method is internal, it can be subject to signature change or be removed, do not use. - * - * @Internal - */ - public RET executeReadRecord(final ORecordId rid, ORecord iRecord, final int recordVersion, - final String fetchPlan, final boolean ignoreCache, final boolean iUpdateCache, final boolean loadTombstones, - final OStorage.LOCKING_STRATEGY lockingStrategy, RecordReader recordReader) { - checkOpeness(); - checkIfActive(); - - getMetadata().makeThreadLocalSchemaSnapshot(); - ORecordSerializationContext.pushContext(); - try { - checkSecurity(ORule.ResourceGeneric.CLUSTER, ORole.PERMISSION_READ, getClusterNameById(rid.getClusterId())); - - // SEARCH IN LOCAL TX - ORecord record = getTransaction().getRecord(rid); - if (record == OTransactionRealAbstract.DELETED_RECORD) - // DELETED IN TX - return null; - - if (record == null && !ignoreCache) - // SEARCH INTO THE CACHE - record = getLocalCache().findRecord(rid); - - if (record != null) { - if (iRecord != null) { - iRecord.fromStream(record.toStream()); - ORecordInternal.setVersion(iRecord, record.getVersion()); - record = iRecord; - } - - OFetchHelper.checkFetchPlanValid(fetchPlan); - if (callbackHooks(ORecordHook.TYPE.BEFORE_READ, record) == ORecordHook.RESULT.SKIP) - return null; - - if (record.getInternalStatus() == ORecordElement.STATUS.NOT_LOADED) - record.reload(); - - if (lockingStrategy == OStorage.LOCKING_STRATEGY.KEEP_SHARED_LOCK) { - OLogManager.instance() - .warn(this, "You use deprecated record locking strategy: %s it may lead to deadlocks " + lockingStrategy); - record.lock(false); - - } else if (lockingStrategy == OStorage.LOCKING_STRATEGY.KEEP_EXCLUSIVE_LOCK) { - OLogManager.instance() - .warn(this, "You use deprecated record locking strategy: %s it may lead to deadlocks " + lockingStrategy); - record.lock(true); - } - - callbackHooks(ORecordHook.TYPE.AFTER_READ, record); - if (record instanceof ODocument) - ODocumentInternal.checkClass((ODocument) record, this); - return (RET) record; - } - - final ORawBuffer recordBuffer; - if (!rid.isValid()) - recordBuffer = null; - else { - OFetchHelper.checkFetchPlanValid(fetchPlan); - - int version; - if (iRecord != null) - version = iRecord.getVersion(); - else - version = recordVersion; - - recordBuffer = recordReader.readRecord(storage, rid, fetchPlan, ignoreCache, version); - } - - if (recordBuffer == null) - return null; - - if (iRecord == null || ORecordInternal.getRecordType(iRecord) != recordBuffer.recordType) - // NO SAME RECORD TYPE: CAN'T REUSE OLD ONE BUT CREATE A NEW ONE FOR IT - iRecord = Orient.instance().getRecordFactoryManager().newInstance(recordBuffer.recordType); - - ORecordInternal.fill(iRecord, rid, recordBuffer.version, recordBuffer.buffer, false); - - if (iRecord instanceof ODocument) - ODocumentInternal.checkClass((ODocument) iRecord, this); - - if (ORecordVersionHelper.isTombstone(iRecord.getVersion())) - return (RET) iRecord; - - if (callbackHooks(ORecordHook.TYPE.BEFORE_READ, iRecord) == ORecordHook.RESULT.SKIP) - return null; - - iRecord.fromStream(recordBuffer.buffer); - - callbackHooks(ORecordHook.TYPE.AFTER_READ, iRecord); - - if (iUpdateCache) - getLocalCache().updateRecord(iRecord); - - return (RET) iRecord; - } catch (OOfflineClusterException t) { - throw t; - } catch (ORecordNotFoundException t) { - throw t; - } catch (Throwable t) { - if (rid.isTemporary()) - throw OException.wrapException(new ODatabaseException("Error on retrieving record using temporary RID: " + rid), t); - else - throw OException.wrapException(new ODatabaseException( - "Error on retrieving record " + rid + " (cluster: " + storage.getPhysicalClusterNameById(rid.clusterId) + ")"), t); - } finally { - ORecordSerializationContext.pullContext(); - getMetadata().clearThreadLocalSchemaSnapshot(); - } - } - - public int assignAndCheckCluster(ORecord record, String iClusterName) { - ORecordId rid = (ORecordId) record.getIdentity(); - // if provided a cluster name use it. - if (rid.clusterId <= ORID.CLUSTER_POS_INVALID && iClusterName != null) { - rid.clusterId = getClusterIdByName(iClusterName); - if (rid.clusterId == -1) - throw new IllegalArgumentException("Cluster name '" + iClusterName + "' is not configured"); - - } - OClass schemaClass = null; - // if cluster id is not set yet try to find it out - if (rid.getClusterId() <= ORID.CLUSTER_ID_INVALID && storage.isAssigningClusterIds()) { - if (record instanceof ODocument) { - schemaClass = ODocumentInternal.getImmutableSchemaClass(((ODocument) record)); - if (schemaClass != null) { - if (schemaClass.isAbstract()) - throw new OSchemaException("Document belongs to abstract class " + schemaClass.getName() + " and cannot be saved"); - rid.clusterId = schemaClass.getClusterForNewInstance((ODocument) record); - } else - rid.clusterId = getDefaultClusterId(); - } else { - rid.clusterId = getDefaultClusterId(); - if (record instanceof OBlob && rid.clusterId != ORID.CLUSTER_ID_INVALID) { - // Set blobClusters = getMetadata().getSchema().getBlobClusters(); - // if (!blobClusters.contains(rid.clusterId) && rid.clusterId != getDefaultClusterId() && rid.clusterId != 0) { - // if (iClusterName == null) - // iClusterName = getClusterNameById(rid.clusterId); - // throw new IllegalArgumentException( - // "Cluster name '" + iClusterName + "' (id=" + rid.clusterId + ") is not configured to store blobs, valid are " - // + blobClusters.toString()); - // } - } - } - } else if (record instanceof ODocument) - schemaClass = ODocumentInternal.getImmutableSchemaClass(((ODocument) record)); - // If the cluster id was set check is validity - if (rid.getClusterId() > ORID.CLUSTER_ID_INVALID) { - if (schemaClass != null) { - String messageClusterName = getClusterNameById(rid.getClusterId()); - checkRecordClass(schemaClass, messageClusterName, rid); - if (!schemaClass.hasClusterId(rid.getClusterId())) { - throw new IllegalArgumentException( - "Cluster name '" + messageClusterName + "' (id=" + rid.getClusterId() + ") is not configured to store the class '" - + schemaClass.getName() + "', valid are " + Arrays.toString(schemaClass.getClusterIds())); - } - } - } - return rid.getClusterId(); - } - - public RET executeSaveEmptyRecord(ORecord record, String clusterName) { - ORecordId rid = (ORecordId) record.getIdentity(); - assert rid.isNew(); - - ORecordInternal.onBeforeIdentityChanged(record); - int id = assignAndCheckCluster(record, clusterName); - clusterName = getClusterNameById(id); - checkSecurity(ORule.ResourceGeneric.CLUSTER, ORole.PERMISSION_CREATE, clusterName); - - byte[] content = getSerializer().writeClassOnly(record); - - final OStorageOperationResult ppos = storage - .createRecord(rid, content, record.getVersion(), recordType, OPERATION_MODE.SYNCHRONOUS.ordinal(), null); - - ORecordInternal.setVersion(record, ppos.getResult().recordVersion); - ((ORecordId) record.getIdentity()).copyFrom(rid); - ORecordInternal.onAfterIdentityChanged(record); - - return (RET) record; - } - - /** - * This method is internal, it can be subject to signature change or be removed, do not use. - * - * @Internal - */ - public RET executeSaveRecord(final ORecord record, String clusterName, final int ver, - final OPERATION_MODE mode, boolean forceCreate, final ORecordCallback recordCreatedCallback, - ORecordCallback recordUpdatedCallback) { - checkOpeness(); - checkIfActive(); - if (!record.isDirty()) - return (RET) record; - - final ORecordId rid = (ORecordId) record.getIdentity(); - - if (rid == null) - throw new ODatabaseException( - "Cannot create record because it has no identity. Probably is not a regular record or contains projections of fields rather than a full record"); - - record.setInternalStatus(ORecordElement.STATUS.MARSHALLING); - try { - - byte[] stream = null; - final OStorageOperationResult operationResult; - - getMetadata().makeThreadLocalSchemaSnapshot(); - if (record instanceof ODocument) - ODocumentInternal.checkClass((ODocument) record, this); - ORecordSerializationContext.pushContext(); - final boolean isNew = forceCreate || rid.isNew(); - try { - - final ORecordHook.TYPE triggerType; - if (isNew) { - // NOTIFY IDENTITY HAS CHANGED - ORecordInternal.onBeforeIdentityChanged(record); - int id = assignAndCheckCluster(record, clusterName); - clusterName = getClusterNameById(id); - checkSecurity(ORule.ResourceGeneric.CLUSTER, ORole.PERMISSION_CREATE, clusterName); - triggerType = ORecordHook.TYPE.BEFORE_CREATE; - } else { - clusterName = getClusterNameById(record.getIdentity().getClusterId()); - checkSecurity(ORule.ResourceGeneric.CLUSTER, ORole.PERMISSION_UPDATE, clusterName); - triggerType = ORecordHook.TYPE.BEFORE_UPDATE; - } - stream = getSerializer().toStream(record, false); - - final ORecordHook.RESULT hookResult = callbackHooks(triggerType, record); - - if (hookResult == ORecordHook.RESULT.RECORD_CHANGED) { - if (record instanceof ODocument) - ((ODocument) record).validate(); - stream = updateStream(record); - } else if (hookResult == ORecordHook.RESULT.SKIP_IO) - return (RET) record; - else if (hookResult == ORecordHook.RESULT.RECORD_REPLACED) - // RETURNED THE REPLACED RECORD - return (RET) OHookReplacedRecordThreadLocal.INSTANCE.get(); - - ORecordSaveThreadLocal.setLast(record); - try { - // SAVE IT - boolean updateContent = ORecordInternal.isContentChanged(record); - byte[] content = (stream == null) ? OCommonConst.EMPTY_BYTE_ARRAY : stream; - byte recordType = ORecordInternal.getRecordType(record); - final int modeIndex = mode.ordinal(); - - // CHECK IF RECORD TYPE IS SUPPORTED - Orient.instance().getRecordFactoryManager().getRecordTypeClass(recordType); - - if (forceCreate || ORecordId.isNew(rid.clusterPosition)) { - // CREATE - final OStorageOperationResult ppos = storage - .createRecord(rid, content, ver, recordType, modeIndex, (ORecordCallback) recordCreatedCallback); - operationResult = new OStorageOperationResult(ppos.getResult().recordVersion, ppos.isMoved()); - - } else { - // UPDATE - operationResult = storage.updateRecord(rid, updateContent, content, ver, recordType, modeIndex, recordUpdatedCallback); - } - - final int version = operationResult.getResult(); - - if (isNew) { - // UPDATE INFORMATION: CLUSTER ID+POSITION - ((ORecordId) record.getIdentity()).copyFrom(rid); - // NOTIFY IDENTITY HAS CHANGED - ORecordInternal.onAfterIdentityChanged(record); - // UPDATE INFORMATION: CLUSTER ID+POSITION - } - - if (operationResult.getModifiedRecordContent() != null) - stream = operationResult.getModifiedRecordContent(); - else if (version > record.getVersion() + 1) - // IN CASE OF REMOTE CONFLICT STRATEGY FORCE UNLOAD DUE TO INVALID CONTENT - record.unload(); - - ORecordInternal.fill(record, rid, version, stream, false); - - callbackHookSuccess(record, isNew, stream, operationResult); - } catch (Exception t) { - callbackHookFailure(record, isNew, stream); - throw t; - } - } finally { - callbackHookFinalize(record, isNew, stream); - ORecordSerializationContext.pullContext(); - getMetadata().clearThreadLocalSchemaSnapshot(); - ORecordSaveThreadLocal.removeLast(); - } - - if (stream != null && stream.length > 0 && !operationResult.isMoved()) - // ADD/UPDATE IT IN CACHE IF IT'S ACTIVE - getLocalCache().updateRecord(record); - } catch (OException e) { - throw e; - } catch (Exception t) { - if (!ORecordId.isValid(record.getIdentity().getClusterPosition())) - throw OException - .wrapException(new ODatabaseException("Error on saving record in cluster #" + record.getIdentity().getClusterId()), t); - else - throw OException.wrapException(new ODatabaseException("Error on saving record " + record.getIdentity()), t); - - } finally { - record.setInternalStatus(ORecordElement.STATUS.LOADED); - } - return (RET) record; - } - - /** - * This method is internal, it can be subject to signature change or be removed, do not use. - * - * @Internal - */ - public void executeDeleteRecord(OIdentifiable record, final int iVersion, final boolean iRequired, final OPERATION_MODE iMode, - boolean prohibitTombstones) { - checkOpeness(); - checkIfActive(); - - final ORecordId rid = (ORecordId) record.getIdentity(); - - if (rid == null) - throw new ODatabaseException( - "Cannot delete record because it has no identity. Probably was created from scratch or contains projections of fields rather than a full record"); - - if (!rid.isValid()) - return; - - record = record.getRecord(); - if (record == null) - return; - - checkSecurity(ORule.ResourceGeneric.CLUSTER, ORole.PERMISSION_DELETE, getClusterNameById(rid.clusterId)); - - ORecordSerializationContext.pushContext(); - getMetadata().makeThreadLocalSchemaSnapshot(); - try { - if (record instanceof ODocument) { - ODocumentInternal.checkClass((ODocument) record, this); - } - try { - // if cache is switched off record will be unreachable after delete. - ORecord rec = record.getRecord(); - if (rec != null) { - callbackHooks(ORecordHook.TYPE.BEFORE_DELETE, rec); - - if (rec instanceof ODocument) - ORidBagDeleter.deleteAllRidBags((ODocument) rec); - } - - final OStorageOperationResult operationResult; - try { - if (prohibitTombstones) { - final boolean result = storage.cleanOutRecord(rid, iVersion, iMode.ordinal(), null); - if (!result && iRequired) - throw new ORecordNotFoundException(rid); - operationResult = new OStorageOperationResult(result); - } else { - final OStorageOperationResult result = storage.deleteRecord(rid, iVersion, iMode.ordinal(), null); - if (!result.getResult() && iRequired) - throw new ORecordNotFoundException(rid); - operationResult = new OStorageOperationResult(result.getResult()); - } - - if (!operationResult.isMoved() && rec != null) - callbackHooks(ORecordHook.TYPE.AFTER_DELETE, rec); - else if (rec != null) - callbackHooks(ORecordHook.TYPE.DELETE_REPLICATED, rec); - } catch (Exception t) { - callbackHooks(ORecordHook.TYPE.DELETE_FAILED, rec); - throw t; - } finally { - callbackHooks(ORecordHook.TYPE.FINALIZE_DELETION, rec); - } - - clearDocumentTracking(rec); - - // REMOVE THE RECORD FROM 1 AND 2 LEVEL CACHES - if (!operationResult.isMoved()) { - getLocalCache().deleteRecord(rid); - } - - } catch (OException e) { - // RE-THROW THE EXCEPTION - throw e; - - } catch (Exception t) { - // WRAP IT AS ODATABASE EXCEPTION - throw OException - .wrapException(new ODatabaseException("Error on deleting record in cluster #" + record.getIdentity().getClusterId()), - t); - } - } finally { - ORecordSerializationContext.pullContext(); - getMetadata().clearThreadLocalSchemaSnapshot(); - } - } - - /** - * This method is internal, it can be subject to signature change or be removed, do not use. - * - * @Internal - */ - public boolean executeHideRecord(OIdentifiable record, final OPERATION_MODE iMode) { - checkOpeness(); - checkIfActive(); - - final ORecordId rid = (ORecordId) record.getIdentity(); - - if (rid == null) - throw new ODatabaseException( - "Cannot hide record because it has no identity. Probably was created from scratch or contains projections of fields rather than a full record"); - - if (!rid.isValid()) - return false; - - checkSecurity(ORule.ResourceGeneric.CLUSTER, ORole.PERMISSION_DELETE, getClusterNameById(rid.clusterId)); - - getMetadata().makeThreadLocalSchemaSnapshot(); - if (record instanceof ODocument) - ODocumentInternal.checkClass((ODocument) record, this); - ORecordSerializationContext.pushContext(); - try { - - final OStorageOperationResult operationResult; - operationResult = storage.hideRecord(rid, iMode.ordinal(), null); - - // REMOVE THE RECORD FROM 1 AND 2 LEVEL CACHES - if (!operationResult.isMoved()) - getLocalCache().deleteRecord(rid); - - return operationResult.getResult(); - } finally { - ORecordSerializationContext.pullContext(); - getMetadata().clearThreadLocalSchemaSnapshot(); - } - } - - public ODatabaseDocumentTx begin() { - return begin(OTransaction.TXTYPE.OPTIMISTIC); - } - - public ODatabaseDocumentTx begin(final OTransaction.TXTYPE iType) { - checkOpeness(); - checkIfActive(); - - if (currentTx.isActive()) { - if (iType == OTransaction.TXTYPE.OPTIMISTIC && currentTx instanceof OTransactionOptimistic) { - currentTx.begin(); - return this; - } - - currentTx.rollback(true, 0); - } - - // CHECK IT'S NOT INSIDE A HOOK - if (!inHook.isEmpty()) - throw new IllegalStateException("Cannot begin a transaction while a hook is executing"); - - // WAKE UP LISTENERS - for (ODatabaseListener listener : browseListeners()) - try { - listener.onBeforeTxBegin(this); - } catch (Throwable t) { - OLogManager.instance().error(this, "Error before tx begin", t); - } - - switch (iType) { - case NOTX: - setDefaultTransactionMode(); - break; - - case OPTIMISTIC: - currentTx = new OTransactionOptimistic(this); - break; - - case PESSIMISTIC: - throw new UnsupportedOperationException("Pessimistic transaction"); - } - - currentTx.begin(); - return this; - } - - public void setDefaultTransactionMode() { - if (!(currentTx instanceof OTransactionNoTx)) - currentTx = new OTransactionNoTx(this); - } - - /** - * {@inheritDoc} - */ - @Override - public void freeze(final boolean throwException) { - checkOpeness(); - if (!(getStorage() instanceof OFreezableStorageComponent)) { - OLogManager.instance().error(this, - "Only local paginated storage supports freeze. If you are using remote client please use OServerAdmin instead"); - - return; - } - - final long startTime = Orient.instance().getProfiler().startChrono(); - - final OFreezableStorageComponent storage = getFreezableStorage(); - if (storage != null) { - storage.freeze(throwException); - } - - Orient.instance().getProfiler() - .stopChrono("db." + getName() + ".freeze", "Time to freeze the database", startTime, "db.*.freeze"); - } - - /** - * {@inheritDoc} - */ - @Override - public void freeze() { - checkOpeness(); - if (!(getStorage() instanceof OFreezableStorageComponent)) { - OLogManager.instance().error(this, - "Only local paginated storage supports freeze. " + "If you use remote client please use OServerAdmin instead"); - - return; - } - - final long startTime = Orient.instance().getProfiler().startChrono(); - - final OFreezableStorageComponent storage = getFreezableStorage(); - if (storage != null) { - storage.freeze(false); - } - - Orient.instance().getProfiler() - .stopChrono("db." + getName() + ".freeze", "Time to freeze the database", startTime, "db.*.freeze"); - } - - /** - * {@inheritDoc} - */ - @Override - public void release() { - checkOpeness(); - if (!(getStorage() instanceof OFreezableStorageComponent)) { - OLogManager.instance().error(this, - "Only local paginated storage supports release. If you are using remote client please use OServerAdmin instead"); - return; - } - - final long startTime = Orient.instance().getProfiler().startChrono(); - - final OFreezableStorageComponent storage = getFreezableStorage(); - if (storage != null) { - storage.release(); - } - - Orient.instance().getProfiler() - .stopChrono("db." + getName() + ".release", "Time to release the database", startTime, "db.*.release"); - } - - /** - * Creates a new ODocument. - */ - public ODocument newInstance() { - return new ODocument(); - } - - /** - * Creates a document with specific class. - * - * @param iClassName the name of class that should be used as a class of created document. - * @return new instance of document. - */ - @Override - public ODocument newInstance(final String iClassName) { - return new ODocument(iClassName); - } - - /** - * {@inheritDoc} - */ - public ORecordIteratorClass browseClass(final String iClassName) { - return browseClass(iClassName, true); - } - - /** - * {@inheritDoc} - */ - public ORecordIteratorClass browseClass(final String iClassName, final boolean iPolymorphic) { - if (getMetadata().getImmutableSchemaSnapshot().getClass(iClassName) == null) - throw new IllegalArgumentException("Class '" + iClassName + "' not found in current database"); - - checkSecurity(ORule.ResourceGeneric.CLASS, ORole.PERMISSION_READ, iClassName); - return new ORecordIteratorClass(this, this, iClassName, iPolymorphic, false); - } - - /** - * {@inheritDoc} - */ - @Override - public ORecordIteratorCluster browseCluster(final String iClusterName) { - checkSecurity(ORule.ResourceGeneric.CLUSTER, ORole.PERMISSION_READ, iClusterName); - - return new ORecordIteratorCluster(this, this, getClusterIdByName(iClusterName)); - } - - /** - * {@inheritDoc} - */ - @Override - public Iterable getListeners() { - return getListenersCopy(); - } - - /** - * {@inheritDoc} - */ - @Override - @Deprecated - public ORecordIteratorCluster browseCluster(String iClusterName, long startClusterPosition, long endClusterPosition, - boolean loadTombstones) { - checkSecurity(ORule.ResourceGeneric.CLUSTER, ORole.PERMISSION_READ, iClusterName); - - return new ORecordIteratorCluster(this, this, getClusterIdByName(iClusterName), startClusterPosition, - endClusterPosition, loadTombstones, OStorage.LOCKING_STRATEGY.DEFAULT); - } - - /** - * Saves a document to the database. Behavior depends by the current running transaction if any. If no transaction is running then - * changes apply immediately. If an Optimistic transaction is running then the record will be changed at commit time. The current - * transaction will continue to see the record as modified, while others not. If a Pessimistic transaction is running, then an - * exclusive lock is acquired against the record. Current transaction will continue to see the record as modified, while others - * cannot access to it since it's locked. - *

- * If MVCC is enabled and the version of the document is different by the version stored in the database, then a - * {@link OConcurrentModificationException} exception is thrown.Before to save the document it must be valid following the - * constraints declared in the schema if any (can work also in schema-less mode). To validate the document the - * {@link ODocument#validate()} is called. - * - * @param iRecord Record to save. - * @return The Database instance itself giving a "fluent interface". Useful to call multiple methods in chain. - * @throws OConcurrentModificationException if the version of the document is different by the version contained in the database. - * @throws OValidationException if the document breaks some validation constraints defined in the schema - * @see #setMVCC(boolean), {@link #isMVCC()} - */ - @Override - public RET save(final ORecord iRecord) { - return (RET) save(iRecord, null, OPERATION_MODE.SYNCHRONOUS, false, null, null); - } - - /** - * Saves a document to the database. Behavior depends by the current running transaction if any. If no transaction is running then - * changes apply immediately. If an Optimistic transaction is running then the record will be changed at commit time. The current - * transaction will continue to see the record as modified, while others not. If a Pessimistic transaction is running, then an - * exclusive lock is acquired against the record. Current transaction will continue to see the record as modified, while others - * cannot access to it since it's locked. - *

- * If MVCC is enabled and the version of the document is different by the version stored in the database, then a - * {@link OConcurrentModificationException} exception is thrown.Before to save the document it must be valid following the - * constraints declared in the schema if any (can work also in schema-less mode). To validate the document the - * {@link ODocument#validate()} is called. - * - * @param iRecord Record to save. - * @param iForceCreate Flag that indicates that record should be created. If record with current rid already exists, exception is thrown - * @param iRecordCreatedCallback callback that is called after creation of new record - * @param iRecordUpdatedCallback callback that is called after record update - * @return The Database instance itself giving a "fluent interface". Useful to call multiple methods in chain. - * @throws OConcurrentModificationException if the version of the document is different by the version contained in the database. - * @throws OValidationException if the document breaks some validation constraints defined in the schema - * @see #setMVCC(boolean), {@link #isMVCC()} - */ - @Override - public RET save(final ORecord iRecord, final OPERATION_MODE iMode, boolean iForceCreate, - final ORecordCallback iRecordCreatedCallback, ORecordCallback iRecordUpdatedCallback) { - return save(iRecord, null, iMode, iForceCreate, iRecordCreatedCallback, iRecordUpdatedCallback); - } - - /** - * Saves a document specifying a cluster where to store the record. Behavior depends by the current running transaction if any. If - * no transaction is running then changes apply immediately. If an Optimistic transaction is running then the record will be - * changed at commit time. The current transaction will continue to see the record as modified, while others not. If a Pessimistic - * transaction is running, then an exclusive lock is acquired against the record. Current transaction will continue to see the - * record as modified, while others cannot access to it since it's locked. - *

- * If MVCC is enabled and the version of the document is different by the version stored in the database, then a - * {@link OConcurrentModificationException} exception is thrown. Before to save the document it must be valid following the - * constraints declared in the schema if any (can work also in schema-less mode). To validate the document the - * {@link ODocument#validate()} is called. - * - * @param iRecord Record to save - * @param iClusterName Cluster name where to save the record - * @return The Database instance itself giving a "fluent interface". Useful to call multiple methods in chain. - * @throws OConcurrentModificationException if the version of the document is different by the version contained in the database. - * @throws OValidationException if the document breaks some validation constraints defined in the schema - * @see #setMVCC(boolean), {@link #isMVCC()}, ODocument#validate() - */ - @Override - public RET save(final ORecord iRecord, final String iClusterName) { - return (RET) save(iRecord, iClusterName, OPERATION_MODE.SYNCHRONOUS, false, null, null); - } - - /** - * Saves a document specifying a cluster where to store the record. Behavior depends by the current running transaction if any. If - * no transaction is running then changes apply immediately. If an Optimistic transaction is running then the record will be - * changed at commit time. The current transaction will continue to see the record as modified, while others not. If a Pessimistic - * transaction is running, then an exclusive lock is acquired against the record. Current transaction will continue to see the - * record as modified, while others cannot access to it since it's locked. - *

- * If MVCC is enabled and the version of the document is different by the version stored in the database, then a - * {@link OConcurrentModificationException} exception is thrown. Before to save the document it must be valid following the - * constraints declared in the schema if any (can work also in schema-less mode). To validate the document the - * {@link ODocument#validate()} is called. - * - * @param iRecord Record to save - * @param iClusterName Cluster name where to save the record - * @param iMode Mode of save: synchronous (default) or asynchronous - * @param iForceCreate Flag that indicates that record should be created. If record with current rid already exists, exception is thrown - * @param iRecordCreatedCallback callback that is called after creation of new record - * @param iRecordUpdatedCallback callback that is called after record update - * @return The Database instance itself giving a "fluent interface". Useful to call multiple methods in chain. - * @throws OConcurrentModificationException if the version of the document is different by the version contained in the database. - * @throws OValidationException if the document breaks some validation constraints defined in the schema - * @see #setMVCC(boolean), {@link #isMVCC()}, ODocument#validate() - */ - @Override - public RET save(final ORecord iRecord, String iClusterName, final OPERATION_MODE iMode, - boolean iForceCreate, final ORecordCallback iRecordCreatedCallback, - ORecordCallback iRecordUpdatedCallback) { - checkOpeness(); - - if (!(iRecord instanceof ODocument)) { - assignAndCheckCluster(iRecord, iClusterName); - return (RET) currentTx.saveRecord(iRecord, iClusterName, iMode, iForceCreate, iRecordCreatedCallback, iRecordUpdatedCallback); - } - - ODocument doc = (ODocument) iRecord; - ODocumentInternal.checkClass(doc, this); - if (!getTransaction().isActive() && !storage.isRemote()) - // EXECUTE VALIDATION ONLY IF NOT IN TX - doc.validate(); - ODocumentInternal.convertAllMultiValuesToTrackedVersions(doc); - - if (iForceCreate || !doc.getIdentity().isValid()) { - if (doc.getClassName() != null) - checkSecurity(ORule.ResourceGeneric.CLASS, ORole.PERMISSION_CREATE, doc.getClassName()); - - assignAndCheckCluster(doc, iClusterName); - - } else { - // UPDATE: CHECK ACCESS ON SCHEMA CLASS NAME (IF ANY) - if (doc.getClassName() != null) - checkSecurity(ORule.ResourceGeneric.CLASS, ORole.PERMISSION_UPDATE, doc.getClassName()); - } - - doc = (ODocument) currentTx - .saveRecord(iRecord, iClusterName, iMode, iForceCreate, iRecordCreatedCallback, iRecordUpdatedCallback); - - return (RET) doc; - } - - /** - * Deletes a document. Behavior depends by the current running transaction if any. If no transaction is running then the record is - * deleted immediately. If an Optimistic transaction is running then the record will be deleted at commit time. The current - * transaction will continue to see the record as deleted, while others not. If a Pessimistic transaction is running, then an - * exclusive lock is acquired against the record. Current transaction will continue to see the record as deleted, while others - * cannot access to it since it's locked. - *

- * If MVCC is enabled and the version of the document is different by the version stored in the database, then a - * {@link OConcurrentModificationException} exception is thrown. - * - * @param record record to delete - * @return The Database instance itself giving a "fluent interface". Useful to call multiple methods in chain. - * @see #setMVCC(boolean), {@link #isMVCC()} - */ - public ODatabaseDocumentTx delete(final ORecord record) { - checkOpeness(); - if (record == null) - throw new ODatabaseException("Cannot delete null document"); - - // CHECK ACCESS ON SCHEMA CLASS NAME (IF ANY) - if (record instanceof ODocument && ((ODocument) record).getClassName() != null) - checkSecurity(ORule.ResourceGeneric.CLASS, ORole.PERMISSION_DELETE, ((ODocument) record).getClassName()); - - try { - currentTx.deleteRecord(record, OPERATION_MODE.SYNCHRONOUS); - } catch (OException e) { - throw e; - } catch (Exception e) { - if (record instanceof ODocument) - throw OException.wrapException(new ODatabaseException( - "Error on deleting record " + record.getIdentity() + " of class '" + ((ODocument) record).getClassName() + "'"), e); - else - throw OException.wrapException(new ODatabaseException("Error on deleting record " + record.getIdentity()), e); - } - return this; - } - - /** - * Returns the number of the records of the class iClassName. - */ - public long countClass(final String iClassName) { - return countClass(iClassName, true); - } - - /** - * Returns the number of the records of the class iClassName considering also sub classes if polymorphic is true. - */ - public long countClass(final String iClassName, final boolean iPolymorphic) { - final OClass cls = getMetadata().getImmutableSchemaSnapshot().getClass(iClassName); - - if (cls == null) - throw new IllegalArgumentException("Class '" + iClassName + "' not found in database"); - - long totalOnDb = cls.count(iPolymorphic); - - long deletedInTx = 0; - long addedInTx = 0; - if (getTransaction().isActive()) - for (ORecordOperation op : getTransaction().getAllRecordEntries()) { - if (op.type == ORecordOperation.DELETED) { - final ORecord rec = op.getRecord(); - if (rec != null && rec instanceof ODocument) { - OClass schemaClass = ((ODocument) rec).getSchemaClass(); - if (iPolymorphic) { - if (schemaClass.isSubClassOf(iClassName)) - deletedInTx++; - } else { - if (iClassName.equals(schemaClass.getName()) || iClassName.equals(schemaClass.getShortName())) - deletedInTx++; - } - } - } - if (op.type == ORecordOperation.CREATED) { - final ORecord rec = op.getRecord(); - if (rec != null && rec instanceof ODocument) { - OClass schemaClass = ((ODocument) rec).getSchemaClass(); - if (iPolymorphic) { - if (schemaClass.isSubClassOf(iClassName)) - addedInTx++; - } else { - if (iClassName.equals(schemaClass.getName()) || iClassName.equals(schemaClass.getShortName())) - addedInTx++; - } - } - } - } - - return (totalOnDb + addedInTx) - deletedInTx; - } - - /** - * {@inheritDoc} - */ - @Override - public ODatabase commit() { - return commit(false); - } - - @Override - public ODatabaseDocument commit(boolean force) throws OTransactionException { - checkOpeness(); - checkIfActive(); - - if (!currentTx.isActive()) - return this; - - if (!force && currentTx.amountOfNestedTxs() > 1) { - currentTx.commit(); - return this; - } - - // WAKE UP LISTENERS - for (ODatabaseListener listener : browseListeners()) - try { - listener.onBeforeTxCommit(this); - } catch (Exception e) { - rollback(force); - - OLogManager.instance().error(this, "Cannot commit the transaction: caught exception on execution of %s.onBeforeTxCommit()", - listener.getClass().getName(), e); - throw OException.wrapException(new OTransactionException( - "Cannot commit the transaction: caught exception on execution of " + listener.getClass().getName() - + "#onBeforeTxCommit()"), e); - } - - try { - currentTx.commit(force); - } catch (RuntimeException e) { - OLogManager.instance().debug(this, "Error on transaction commit", e); - - // WAKE UP ROLLBACK LISTENERS - for (ODatabaseListener listener : browseListeners()) - try { - listener.onBeforeTxRollback(this); - } catch (Throwable t) { - OLogManager.instance().error(this, "Error before transaction rollback", t); - } - - // ROLLBACK TX AT DB LEVEL - currentTx.rollback(false, 0); - getLocalCache().clear(); - - // WAKE UP ROLLBACK LISTENERS - for (ODatabaseListener listener : browseListeners()) - try { - listener.onAfterTxRollback(this); - } catch (Throwable t) { - OLogManager.instance().error(this, "Error after transaction rollback", t); - } - throw e; - } - - // WAKE UP LISTENERS - for (ODatabaseListener listener : browseListeners()) - try { - listener.onAfterTxCommit(this); - } catch (Exception e) { - final String message = - "Error after the transaction has been committed. The transaction remains valid. The exception caught was on execution of " - + listener.getClass() + ".onAfterTxCommit()"; - - OLogManager.instance().error(this, message, e); - - throw OException.wrapException(new OTransactionBlockedException(message), e); - - } - - return this; - } - - @Override - public OUncompletedCommit initiateCommit() { - return initiateCommit(false); - } - - @Override - public OUncompletedCommit initiateCommit(boolean force) { - checkOpeness(); - checkIfActive(); - - if (!currentTx.isActive()) - return OUncompletedCommit.NO_OPERATION; - - if (!force && currentTx.amountOfNestedTxs() > 1) - return new UncompletedCommit(false, currentTx.initiateCommit(false)); - - // WAKE UP LISTENERS - for (ODatabaseListener listener : browseListeners()) - try { - listener.onBeforeTxCommit(this); - } catch (Exception e) { - rollback(force); - - OLogManager.instance().error(this, "Cannot commit the transaction: caught exception on execution of %s.onBeforeTxCommit()", - listener.getClass().getName(), e); - throw OException.wrapException(new OTransactionException( - "Cannot commit the transaction: caught exception on execution of " + listener.getClass().getName() - + "#onBeforeTxCommit()"), e); - } - - try { - return new UncompletedCommit(true, currentTx.initiateCommit(force)); - } catch (RuntimeException e) { - OLogManager.instance().debug(this, "Error on transaction commit", e); - - // WAKE UP ROLLBACK LISTENERS - for (ODatabaseListener listener : browseListeners()) - try { - listener.onBeforeTxRollback(this); - } catch (Throwable t) { - OLogManager.instance().error(this, "Error before transaction rollback", t); - } - - // ROLLBACK TX AT DB LEVEL - currentTx.rollback(false, 0); - getLocalCache().clear(); - - // WAKE UP ROLLBACK LISTENERS - for (ODatabaseListener listener : browseListeners()) - try { - listener.onAfterTxRollback(this); - } catch (Throwable t) { - OLogManager.instance().error(this, "Error after transaction rollback", t); - } - throw e; - } - } - - /** - * {@inheritDoc} - */ - @Override - public ODatabase rollback() { - return rollback(false); - } - - @Override - public ODatabaseDocument rollback(boolean force) throws OTransactionException { - checkOpeness(); - if (currentTx.isActive()) { - - if (!force && currentTx.amountOfNestedTxs() > 1) { - currentTx.rollback(); - return this; - } - - // WAKE UP LISTENERS - for (ODatabaseListener listener : browseListeners()) - try { - listener.onBeforeTxRollback(this); - } catch (Throwable t) { - OLogManager.instance().error(this, "Error before transactional rollback", t); - } - - currentTx.rollback(force, -1); - - // WAKE UP LISTENERS - for (ODatabaseListener listener : browseListeners()) - try { - listener.onAfterTxRollback(this); - } catch (Throwable t) { - OLogManager.instance().error(this, "Error after transaction rollback", t); - } - } - - getLocalCache().clear(); - - return this; - } - - /** - * This method is internal, it can be subject to signature change or be removed, do not use. - * - * @Internal - */ - @Override - public DB getUnderlying() { - throw new UnsupportedOperationException(); - } - - /** - * This method is internal, it can be subject to signature change or be removed, do not use. - * - * @Internal - */ - @Override - public OStorage getStorage() { - return storage; - } - - /** - * This method is internal, it can be subject to signature change or be removed, do not use. - * - * @Internal - */ - @Override - public void replaceStorage(OStorage iNewStorage) { - storage = iNewStorage; - } - - @Override - public V callInLock(final Callable iCallable, final boolean iExclusiveLock) { - return storage.callInLock(iCallable, iExclusiveLock); - } - - @Override - public List backup(final OutputStream out, final Map options, final Callable callable, - final OCommandOutputListener iListener, final int compressionLevel, final int bufferSize) throws IOException { - return storage.backup(out, options, callable, iListener, compressionLevel, bufferSize); - } - - @Override - public void restore(final InputStream in, final Map options, final Callable callable, - final OCommandOutputListener iListener) throws IOException { - if (storage == null) - storage = Orient.instance().loadStorage(url); - - getStorage().restore(in, options, callable, iListener); - - if (!isClosed()) { - loadMetadata(); - sharedContext = null; - } - } - - /** - * {@inheritDoc} - */ - public OSBTreeCollectionManager getSbTreeCollectionManager() { - return getStorage().getSBtreeCollectionManager(); - } - - @Override - public OCurrentStorageComponentsFactory getStorageVersions() { - return componentsFactory; - } - - public ORecordSerializer getSerializer() { - return serializer; - } - - /** - * Sets serializer for the database which will be used for document serialization. - * - * @param serializer the serializer to set. - */ - public void setSerializer(ORecordSerializer serializer) { - this.serializer = serializer; - } - - @Override - public void resetInitialization() { - for (ORecordHook h : hooks.keySet()) - h.onUnregister(); - - hooks.clear(); - compileHooks(); - - close(); - - initialized = false; - } - - @Override - public String incrementalBackup(final String path) { - checkOpeness(); - checkIfActive(); - - return storage.incrementalBackup(path); - } - - @Override - @Deprecated - public DB checkSecurity(final String iResource, final int iOperation) { - final String resourceSpecific = ORule.mapLegacyResourceToSpecificResource(iResource); - final ORule.ResourceGeneric resourceGeneric = ORule.mapLegacyResourceToGenericResource(iResource); - - if (resourceSpecific == null || resourceSpecific.equals("*")) - checkSecurity(resourceGeneric, null, iOperation); - - return checkSecurity(resourceGeneric, resourceSpecific, iOperation); - } - - @Override - @Deprecated - public DB checkSecurity(final String iResourceGeneric, final int iOperation, - final Object iResourceSpecific) { - final ORule.ResourceGeneric resourceGeneric = ORule.mapLegacyResourceToGenericResource(iResourceGeneric); - if (iResourceSpecific == null || iResourceSpecific.equals("*")) - return checkSecurity(resourceGeneric, iOperation, (Object) null); - - return checkSecurity(resourceGeneric, iOperation, iResourceSpecific); - } - - @Override - @Deprecated - public DB checkSecurity(final String iResourceGeneric, final int iOperation, - final Object... iResourcesSpecific) { - final ORule.ResourceGeneric resourceGeneric = ORule.mapLegacyResourceToGenericResource(iResourceGeneric); - return checkSecurity(resourceGeneric, iOperation, iResourcesSpecific); - } - - /** - * @return true if database is obtained from the pool and false otherwise. - */ - @Override - public boolean isPooled() { - return false; - } - - /** - * Use #activateOnCurrentThread instead. - */ - @Deprecated - public void setCurrentDatabaseInThreadLocal() { - activateOnCurrentThread(); - } - - /** - * Activates current database instance on current thread. - */ - @Override - public ODatabaseDocumentTx activateOnCurrentThread() { - final ODatabaseRecordThreadLocal tl = ODatabaseRecordThreadLocal.INSTANCE; - if (tl != null) - tl.set(this); - return this; - } - - @Override - public boolean isActiveOnCurrentThread() { - final ODatabaseRecordThreadLocal tl = ODatabaseRecordThreadLocal.INSTANCE; - final ODatabaseDocumentInternal db = tl != null ? tl.getIfDefined() : null; - return db == this; - } - - protected void checkOpeness() { - if (status == STATUS.CLOSED) - throw new ODatabaseException("Database '" + getURL() + "' is closed"); - } - - private void popInHook(OIdentifiable id) { - inHook.remove(id); - } - - private boolean pushInHook(OIdentifiable id) { - return inHook.add(id); - } - - private void initAtFirstOpen(String iUserName, String iUserPassword) { - if (initialized) - return; - - ORecordSerializerFactory serializerFactory = ORecordSerializerFactory.instance(); - String serializeName = getStorage().getConfiguration().getRecordSerializer(); - if (serializeName == null) - serializeName = ORecordSerializerSchemaAware2CSV.NAME; - serializer = serializerFactory.getFormat(serializeName); - if (serializer == null) - throw new ODatabaseException("RecordSerializer with name '" + serializeName + "' not found "); - if (getStorage().getConfiguration().getRecordSerializerVersion() > serializer.getMinSupportedVersion()) - throw new ODatabaseException("Persistent record serializer version is not support by the current implementation"); - - componentsFactory = getStorage().getComponentsFactory(); - - localCache.startup(); - - user = null; - - loadMetadata(); - - if (!(getStorage() instanceof OStorageProxy)) { - if (metadata.getIndexManager().autoRecreateIndexesAfterCrash()) { - metadata.getIndexManager().recreateIndexes(); - - activateOnCurrentThread(); - user = null; - } - - installHooksEmbedded(); - registerHook(new OCommandCacheHook(this), ORecordHook.HOOK_POSITION.REGULAR); - registerHook(new OSecurityTrackerHook(metadata.getSecurity(), this), ORecordHook.HOOK_POSITION.LAST); - - user = null; - } else if (iUserName != null && iUserPassword != null) { - user = new OImmutableUser(-1, new OUser(iUserName, OUser.encryptPassword(iUserPassword)) - .addRole(new ORole("passthrough", null, ORole.ALLOW_MODES.ALLOW_ALL_BUT))); - installHooksRemote(); - } - - initialized = true; - } - - protected void installHooksEmbedded() { - hooks.clear(); - registerHook(new OClassTrigger(this), ORecordHook.HOOK_POSITION.FIRST); - registerHook(new ORestrictedAccessHook(this), ORecordHook.HOOK_POSITION.FIRST); - registerHook(new OUserTrigger(this), ORecordHook.HOOK_POSITION.EARLY); - registerHook(new OFunctionTrigger(this), ORecordHook.HOOK_POSITION.REGULAR); - registerHook(new OSequenceTrigger(this), ORecordHook.HOOK_POSITION.REGULAR); - registerHook(new OClassIndexManager(this), ORecordHook.HOOK_POSITION.LAST); - registerHook(new OSchedulerTrigger(this), ORecordHook.HOOK_POSITION.LAST); - registerHook(new OLiveQueryHook(this), ORecordHook.HOOK_POSITION.LAST); - } - - protected void installHooksRemote() { - hooks.clear(); - registerHook(new ClassIndexManagerRemote(this), ORecordHook.HOOK_POSITION.LAST); - } - - private void closeOnDelete() { - if (status != STATUS.OPEN) - return; - - if (currentIntent != null) { - currentIntent.end(this); - currentIntent = null; - } - - resetListeners(); - - if (storage != null) - storage.close(true, true); - - storage = null; - status = STATUS.CLOSED; - } - - private void clearCustomInternal() { - storage.getConfiguration().clearProperties(); - } - - private void removeCustomInternal(final String iName) { - setCustomInternal(iName, null); - } - - private void setCustomInternal(final String iName, final String iValue) { - if (iValue == null || "null".equalsIgnoreCase(iValue)) - // REMOVE - storage.getConfiguration().removeProperty(iName); - else - // SET - storage.getConfiguration().setProperty(iName, iValue); - - storage.getConfiguration().update(); - } - - private void callbackHookFailure(ORecord record, boolean wasNew, byte[] stream) { - if (stream != null && stream.length > 0) - callbackHooks(wasNew ? ORecordHook.TYPE.CREATE_FAILED : ORecordHook.TYPE.UPDATE_FAILED, record); - } - - private void callbackHookSuccess(final ORecord record, final boolean wasNew, final byte[] stream, - final OStorageOperationResult operationResult) { - if (stream != null && stream.length > 0) { - final ORecordHook.TYPE hookType; - if (!operationResult.isMoved()) { - hookType = wasNew ? ORecordHook.TYPE.AFTER_CREATE : ORecordHook.TYPE.AFTER_UPDATE; - } else { - hookType = wasNew ? ORecordHook.TYPE.CREATE_REPLICATED : ORecordHook.TYPE.UPDATE_REPLICATED; - } - callbackHooks(hookType, record); - - } - } - - private void callbackHookFinalize(final ORecord record, final boolean wasNew, final byte[] stream) { - if (stream != null && stream.length > 0) { - final ORecordHook.TYPE hookType; - hookType = wasNew ? ORecordHook.TYPE.FINALIZE_CREATION : ORecordHook.TYPE.FINALIZE_UPDATE; - callbackHooks(hookType, record); - - clearDocumentTracking(record); - } - } - - private void clearDocumentTracking(final ORecord record) { - if (record instanceof ODocument && ((ODocument) record).isTrackingChanges()) { - ODocumentInternal.clearTrackData((ODocument) record); - } - } - - private void checkRecordClass(final OClass recordClass, final String iClusterName, final ORecordId rid) { - if (getStorageVersions().classesAreDetectedByClusterId()) { - final OClass clusterIdClass = metadata.getImmutableSchemaSnapshot().getClassByClusterId(rid.clusterId); - if (recordClass == null && clusterIdClass != null || clusterIdClass == null && recordClass != null || (recordClass != null - && !recordClass.equals(clusterIdClass))) - throw new IllegalArgumentException( - "Record saved into cluster '" + iClusterName + "' should be saved with class '" + clusterIdClass - + "' but has been created with class '" + recordClass + "'"); - } - } - - private byte[] updateStream(final ORecord record) { - ORecordSerializationContext.pullContext(); - - ODirtyManager manager = ORecordInternal.getDirtyManager(record); - Set newRecords = manager.getNewRecords(); - Set updatedRecords = manager.getUpdateRecords(); - manager.clearForSave(); - if (newRecords != null) { - for (ORecord newRecord : newRecords) { - if (newRecord != record) - getTransaction().saveRecord(newRecord, null, OPERATION_MODE.SYNCHRONOUS, false, null, null); - } - } - if (updatedRecords != null) { - for (ORecord updatedRecord : updatedRecords) { - if (updatedRecord != record) - getTransaction().saveRecord(updatedRecord, null, OPERATION_MODE.SYNCHRONOUS, false, null, null); - } - } - - ORecordSerializationContext.pushContext(); - ORecordInternal.unsetDirty(record); - record.setDirty(); - return serializer.toStream(record, false); - } - - protected void init() { - currentTx = new OTransactionNoTx(this); - } - - private OFreezableStorageComponent getFreezableStorage() { - OStorage s = getStorage(); - if (s instanceof OFreezableStorageComponent) - return (OFreezableStorageComponent) s; - else { - OLogManager.instance().error(this, "Storage of type " + s.getType() + " does not support freeze operation"); - return null; - } - } - - public void checkIfActive() { - final ODatabaseRecordThreadLocal tl = ODatabaseRecordThreadLocal.INSTANCE; - final ODatabaseDocumentInternal currentDatabase = tl != null ? tl.get() : null; - if (currentDatabase != this) - throw new IllegalStateException( - "The current database instance (" + toString() + ") is not active on the current thread (" + Thread.currentThread() - + "). Current active database is: " + currentDatabase); - } - - @Override - public int addBlobCluster(final String iClusterName, final Object... iParameters) { - int id; - if (getStorage() instanceof OStorageProxy) { - id = command(new OCommandSQL("create blob cluster :1")).execute(iClusterName); - getMetadata().getSchema().reload(); - } else { - if (!existsCluster(iClusterName)) { - id = addCluster(iClusterName, iParameters); - } else - id = getClusterIdByName(iClusterName); - getMetadata().getSchema().addBlobCluster(id); - } - return id; - } - - public Set getBlobClusterIds() { - return getMetadata().getSchema().getBlobClusters(); - } - - private class UncompletedCommit implements OUncompletedCommit { - - private final boolean topLevel; - private final OUncompletedCommit nestedCommit; - - public UncompletedCommit(boolean topLevel, OUncompletedCommit nestedCommit) { - this.topLevel = topLevel; - this.nestedCommit = nestedCommit; - } - - @Override - public Void complete() { - checkOpeness(); - checkIfActive(); - - if (!topLevel) { - nestedCommit.complete(); - return null; - } - - try { - nestedCommit.complete(); - } catch (RuntimeException e) { - OLogManager.instance().debug(this, "Error on transaction commit", e); - - // WAKE UP ROLLBACK LISTENERS - for (ODatabaseListener listener : browseListeners()) - try { - listener.onBeforeTxRollback(ODatabaseDocumentTx.this); - } catch (Throwable t) { - OLogManager.instance().error(this, "Error before transaction rollback", t); - } - - // ROLLBACK TX AT DB LEVEL - nestedCommit.rollback(); - getLocalCache().clear(); - - // WAKE UP ROLLBACK LISTENERS - for (ODatabaseListener listener : browseListeners()) - try { - listener.onAfterTxRollback(ODatabaseDocumentTx.this); - } catch (Throwable t) { - OLogManager.instance().error(this, "Error after transaction rollback", t); - } - throw e; - } - - // WAKE UP LISTENERS - for (ODatabaseListener listener : browseListeners()) - try { - listener.onAfterTxCommit(ODatabaseDocumentTx.this); - } catch (Exception e) { - final String message = - "Error after the transaction has been committed. The transaction remains valid. The exception caught was on execution of " - + listener.getClass() + ".onAfterTxCommit()"; - - OLogManager.instance().error(this, message, e); - - throw OException.wrapException(new OTransactionBlockedException(message), e); - } - - return null; - } - - @Override - public void rollback() { - checkOpeness(); - checkIfActive(); - - if (!topLevel) { - nestedCommit.rollback(); - return; - } - - // WAKE UP ROLLBACK LISTENERS - for (ODatabaseListener listener : browseListeners()) - try { - listener.onBeforeTxRollback(ODatabaseDocumentTx.this); - } catch (Throwable t) { - OLogManager.instance().error(this, "Error before transaction rollback", t); - } - - // ROLLBACK TX AT DB LEVEL - nestedCommit.rollback(); - getLocalCache().clear(); - - // WAKE UP ROLLBACK LISTENERS - for (ODatabaseListener listener : browseListeners()) - try { - listener.onAfterTxRollback(ODatabaseDocumentTx.this); - } catch (Throwable t) { - OLogManager.instance().error(this, "Error after transaction rollback", t); - } - } - } - - private void compileHooks() { - final List[] intermediateHooksByScope = new List[ORecordHook.SCOPE.values().length]; - for (ORecordHook.SCOPE scope : ORecordHook.SCOPE.values()) - intermediateHooksByScope[scope.ordinal()] = new ArrayList<>(); - - for (ORecordHook hook : hooks.keySet()) - for (ORecordHook.SCOPE scope : hook.getScopes()) - intermediateHooksByScope[scope.ordinal()].add(hook); - - for (ORecordHook.SCOPE scope : ORecordHook.SCOPE.values()) { - final int ordinal = scope.ordinal(); - final List scopeHooks = intermediateHooksByScope[ordinal]; - hooksByScope[ordinal] = scopeHooks.toArray(new ORecordHook[scopeHooks.size()]); - } - } - - @Override - public OSharedContext getSharedContext() { - // NOW NEED TO GET THE CONTEXT FROM RESOURCES IN FUTURE WILL BE NOT NEEDED - if (sharedContext == null) { - sharedContext = storage.getResource(OSharedContext.class.getName(), new Callable() { - @Override - public OSharedContext call() throws Exception { - throw new ODatabaseException("Accessing to the database context before the database has bean initialized"); - } - }); - } - return sharedContext; - } - - public OTodoResultSet query(String query, Object... args) { - return getStorage().query(this, query, args); - } - - public OTodoResultSet query(String query, Map args) { - return getStorage().query(this, query, args); - } - - public OTodoResultSet command(String query, Object... args) { - return getStorage().command(this, query, args); - } - - public OTodoResultSet command(String query, Map args) { - return getStorage().command(this, query, args); - } - - public static Object executeWithRetries(final OCallable callback, final int maxRetry) { - return executeWithRetries(callback, maxRetry, 0); - } - - public static Object executeWithRetries(final OCallable callback, final int maxRetry, - final int waitBetweenRetry) { - ONeedRetryException lastException = null; - for (int retry = 0; retry < maxRetry; ++retry) { - try { - return callback.call(retry); - } catch (ONeedRetryException e) { - // SAVE LAST EXCEPTION AND RETRY - lastException = e; - - if (waitBetweenRetry > 0) - try { - Thread.sleep(waitBetweenRetry); - } catch (InterruptedException e1) { - Thread.currentThread().interrupt(); - break; - } - } - } - throw lastException; - } - - private void bindPropertiesToContext(OContextConfiguration configuration, final Map iProperties) { - final String connectionStrategy = iProperties != null ? (String) iProperties.get("connectionStrategy") : null; - if (connectionStrategy != null) - configuration.setValue(OGlobalConfiguration.CLIENT_CONNECTION_STRATEGY, connectionStrategy); - - final String compressionMethod = iProperties != null ? - (String) iProperties.get(OGlobalConfiguration.STORAGE_COMPRESSION_METHOD.getKey().toLowerCase()) : - null; - if (compressionMethod != null) - // SAVE COMPRESSION METHOD IN CONFIGURATION - configuration.setValue(OGlobalConfiguration.STORAGE_COMPRESSION_METHOD, compressionMethod); - - final String encryptionMethod = iProperties != null ? - (String) iProperties.get(OGlobalConfiguration.STORAGE_ENCRYPTION_METHOD.getKey().toLowerCase()) : - null; - if (encryptionMethod != null) - // SAVE ENCRYPTION METHOD IN CONFIGURATION - configuration.setValue(OGlobalConfiguration.STORAGE_ENCRYPTION_METHOD, encryptionMethod); - - final String encryptionKey = - iProperties != null ? (String) iProperties.get(OGlobalConfiguration.STORAGE_ENCRYPTION_KEY.getKey().toLowerCase()) : null; - if (encryptionKey != null) - // SAVE ENCRYPTION KEY IN CONFIGURATION - configuration.setValue(OGlobalConfiguration.STORAGE_ENCRYPTION_KEY, encryptionKey); - } - - private void bindPropertiesToContextGlobal(OContextConfiguration configuration, - final Map iProperties) { - final String connectionStrategy = iProperties != null ? (String) iProperties.get("connectionStrategy") : null; - if (connectionStrategy != null) - configuration.setValue(OGlobalConfiguration.CLIENT_CONNECTION_STRATEGY, connectionStrategy); - - final String compressionMethod = - iProperties != null ? (String) iProperties.get(OGlobalConfiguration.STORAGE_COMPRESSION_METHOD) : null; - if (compressionMethod != null) - // SAVE COMPRESSION METHOD IN CONFIGURATION - configuration.setValue(OGlobalConfiguration.STORAGE_COMPRESSION_METHOD, compressionMethod); - - final String encryptionMethod = - iProperties != null ? (String) iProperties.get(OGlobalConfiguration.STORAGE_ENCRYPTION_METHOD) : null; - if (encryptionMethod != null) - // SAVE ENCRYPTION METHOD IN CONFIGURATION - configuration.setValue(OGlobalConfiguration.STORAGE_ENCRYPTION_METHOD, encryptionMethod); - - final String encryptionKey = iProperties != null ? (String) iProperties.get(OGlobalConfiguration.STORAGE_ENCRYPTION_KEY) : null; - if (encryptionKey != null) - // SAVE ENCRYPTION KEY IN CONFIGURATION - configuration.setValue(OGlobalConfiguration.STORAGE_ENCRYPTION_KEY, encryptionKey); - } - -} +package com.orientechnologies.orient.core.db.document; + +import com.orientechnologies.orient.core.OUncompletedCommit; +import com.orientechnologies.orient.core.cache.OLocalRecordCache; +import com.orientechnologies.orient.core.command.OCommandOutputListener; +import com.orientechnologies.orient.core.command.OCommandRequest; +import com.orientechnologies.orient.core.config.OContextConfiguration; +import com.orientechnologies.orient.core.config.OGlobalConfiguration; +import com.orientechnologies.orient.core.conflict.ORecordConflictStrategy; +import com.orientechnologies.orient.core.db.*; +import com.orientechnologies.orient.core.db.record.OCurrentStorageComponentsFactory; +import com.orientechnologies.orient.core.db.record.OIdentifiable; +import com.orientechnologies.orient.core.db.record.ridbag.sbtree.OSBTreeCollectionManager; +import com.orientechnologies.orient.core.dictionary.ODictionary; +import com.orientechnologies.orient.core.exception.OCommandExecutionException; +import com.orientechnologies.orient.core.exception.ORecordNotFoundException; +import com.orientechnologies.orient.core.exception.OTransactionException; +import com.orientechnologies.orient.core.hook.ORecordHook; +import com.orientechnologies.orient.core.id.ORID; +import com.orientechnologies.orient.core.id.ORecordId; +import com.orientechnologies.orient.core.intent.OIntent; +import com.orientechnologies.orient.core.iterator.ORecordIteratorClass; +import com.orientechnologies.orient.core.iterator.ORecordIteratorCluster; +import com.orientechnologies.orient.core.metadata.OMetadataInternal; +import com.orientechnologies.orient.core.metadata.schema.OType; +import com.orientechnologies.orient.core.metadata.security.ORule; +import com.orientechnologies.orient.core.metadata.security.OSecurityUser; +import com.orientechnologies.orient.core.metadata.security.OToken; +import com.orientechnologies.orient.core.query.OQuery; +import com.orientechnologies.orient.core.record.ORecord; +import com.orientechnologies.orient.core.record.impl.ODocument; +import com.orientechnologies.orient.core.serialization.serializer.binary.OBinarySerializerFactory; +import com.orientechnologies.orient.core.serialization.serializer.record.ORecordSerializer; +import com.orientechnologies.orient.core.serialization.serializer.record.binary.ORecordSerializerBinary; +import com.orientechnologies.orient.core.serialization.serializer.record.string.ORecordSerializerSchemaAware2CSV; +import com.orientechnologies.orient.core.sql.OCommandSQLParsingException; +import com.orientechnologies.orient.core.sql.executor.OTodoResultSet; +import com.orientechnologies.orient.core.storage.ORecordCallback; +import com.orientechnologies.orient.core.storage.ORecordMetadata; +import com.orientechnologies.orient.core.storage.OStorage; +import com.orientechnologies.orient.core.tx.OTransaction; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.*; +import java.util.concurrent.Callable; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import static jdk.nashorn.internal.runtime.regexp.joni.constants.EncloseType.MEMORY; + +/** + * Created by tglman on 20/07/16. + */ +public class ODatabaseDocumentTx implements ODatabaseDocumentInternal { + + private static ConcurrentMap embedded = new ConcurrentHashMap<>(); + private static ConcurrentMap remote = new ConcurrentHashMap<>(); + + private ODatabaseDocumentInternal internal; + private final String url; + private OrientDBFactory factory; + private final String type; + private final String dbName; + private final String baseUrl; + + public ODatabaseDocumentTx(String url) { + this.url = url; + int typeIndex = url.indexOf(':'); + String remoteUrl = url.substring(typeIndex + 1); + type = url.substring(0, typeIndex); + int index = remoteUrl.lastIndexOf('/'); + if (index > 0) { + baseUrl = remoteUrl.substring(0, index); + dbName = remoteUrl.substring(index + 1); + } else { + baseUrl = "./"; + dbName = remoteUrl; + } + } + + private ODatabaseDocumentTx(ODatabaseDocumentTx other) { + url = other.url; + type = other.type; + baseUrl = other.baseUrl; + dbName = other.dbName; + internal = other.internal.copy(); + } + + public static ORecordSerializer getDefaultSerializer() { + return ODatabaseDocumentTxOrig.getDefaultSerializer(); + } + + public static void setDefaultSerializer(ORecordSerializer defaultSerializer) { + ODatabaseDocumentTxOrig.setDefaultSerializer(defaultSerializer); + } + + @Override + public OCurrentStorageComponentsFactory getStorageVersions() { + return internal.getStorageVersions(); + } + + @Override + public OSBTreeCollectionManager getSbTreeCollectionManager() { + return internal.getSbTreeCollectionManager(); + } + + @Override + public OBinarySerializerFactory getSerializerFactory() { + return internal.getSerializerFactory(); + } + + @Override + public ORecordSerializer getSerializer() { + return internal.getSerializer(); + } + + @Override + public int assignAndCheckCluster(ORecord record, String iClusterName) { + return internal.assignAndCheckCluster(record, iClusterName); + } + + @Override + public RET loadIfVersionIsNotLatest(ORID rid, int recordVersion, String fetchPlan, boolean ignoreCache) + throws ORecordNotFoundException { + return (RET) internal.loadIfVersionIsNotLatest(rid, recordVersion, fetchPlan, ignoreCache); + } + + @Override + public void reloadUser() { + internal.reloadUser(); + } + + @Override + public ORecordHook.RESULT callbackHooks(ORecordHook.TYPE type, OIdentifiable id) { + return internal.callbackHooks(type, id); + } + + @Override + public RET executeReadRecord(ORecordId rid, ORecord iRecord, int recordVersion, String fetchPlan, + boolean ignoreCache, boolean iUpdateCache, boolean loadTombstones, OStorage.LOCKING_STRATEGY lockingStrategy, + RecordReader recordReader) { + return internal + .executeReadRecord(rid, iRecord, recordVersion, fetchPlan, ignoreCache, iUpdateCache, loadTombstones, lockingStrategy, + recordReader); + } + + @Override + public RET executeSaveRecord(ORecord record, String clusterName, int ver, OPERATION_MODE mode, + boolean forceCreate, ORecordCallback recordCreatedCallback, + ORecordCallback recordUpdatedCallback) { + return internal.executeSaveRecord(record, clusterName, ver, mode, forceCreate, recordCreatedCallback, recordUpdatedCallback); + } + + @Override + public void executeDeleteRecord(OIdentifiable record, int iVersion, boolean iRequired, OPERATION_MODE iMode, + boolean prohibitTombstones) { + internal.executeDeleteRecord(record, iVersion, iRequired, iMode, prohibitTombstones); + } + + @Override + public RET executeSaveEmptyRecord(ORecord record, String clusterName) { + return internal.executeSaveEmptyRecord(record, clusterName); + } + + @Override + public void setDefaultTransactionMode() { + internal.setDefaultTransactionMode(); + } + + @Override + public OMetadataInternal getMetadata() { + return internal.getMetadata(); + } + + @Override + public > DB registerHook(ORecordHook iHookImpl) { + internal.registerHook(iHookImpl); + return (DB) this; + } + + @Override + public > DB registerHook(ORecordHook iHookImpl, ORecordHook.HOOK_POSITION iPosition) { + internal.registerHook(iHookImpl, iPosition); + return (DB) this; + } + + @Override + public Map getHooks() { + return internal.getHooks(); + } + + @Override + public > DB unregisterHook(ORecordHook iHookImpl) { + internal.unregisterHook(iHookImpl); + return (DB) this; + } + + @Override + public boolean isMVCC() { + return false; + } + + @Override + public Iterable getListeners() { + return internal.getListeners(); + } + + @Override + public > DB setMVCC(boolean iValue) { + return null; + } + + @Override + public String getType() { + return internal.getType(); + } + + @Override + public ORecordConflictStrategy getConflictStrategy() { + return internal.getConflictStrategy(); + } + + @Override + public > DB setConflictStrategy(String iStrategyName) { + internal.setConflictStrategy(iStrategyName); + return (DB) this; + } + + @Override + public > DB setConflictStrategy(ORecordConflictStrategy iResolver) { + internal.setConflictStrategy(iResolver); + return (DB) this; + } + + @Override + public String incrementalBackup(String path) { + return internal.incrementalBackup(path); + } + + @Override + public ODatabaseDocumentTx copy() { + return new ODatabaseDocumentTx(this); + } + + @Override + public Set executeReadRecords(Set iRids, boolean ignoreCache) { + return internal.executeReadRecords(iRids, ignoreCache); + } + + @Override + public void checkIfActive() { + internal.checkIfActive(); + } + + @Override + public void callOnOpenListeners() { + internal.callOnOpenListeners(); + } + + @Override + public void callOnCloseListeners() { + internal.callOnOpenListeners(); + } + + @Override + public OStorage getStorage() { + if (internal == null) + return null; + return internal.getStorage(); + } + + @Override + public void setUser(OSecurityUser user) { + internal.setUser(user); + } + + @Override + public void replaceStorage(OStorage iNewStorage) { + internal.replaceStorage(iNewStorage); + } + + @Override + public V callInLock(Callable iCallable, boolean iExclusiveLock) { + return internal.callInLock(iCallable, iExclusiveLock); + } + + @Override + public void resetInitialization() { + internal.resetInitialization(); + } + + @Override + public ODatabaseInternal getDatabaseOwner() { + internal.getDatabaseOwner(); + return this; + } + + @Override + public ODatabaseInternal setDatabaseOwner(ODatabaseInternal iOwner) { + internal.setDatabaseOwner(iOwner); + return this; + } + + @Override + public DB getUnderlying() { + return internal.getUnderlying(); + } + + @Override + public void setInternal(ATTRIBUTES attribute, Object iValue) { + internal.setInternal(attribute, iValue); + } + + @Override + public DB open(OToken iToken) { + throw new UnsupportedOperationException(); + } + + @Override + public OUncompletedCommit initiateCommit() { + return internal.initiateCommit(); + } + + @Override + public OUncompletedCommit initiateCommit(boolean force) { + return internal.initiateCommit(force); + } + + @Override + public OSharedContext getSharedContext() { + return internal.getSharedContext(); + } + + @Override + public ORecordIteratorClass browseClass(String iClassName) { + return internal.browseClass(iClassName); + } + + @Override + public ORecordIteratorClass browseClass(String iClassName, boolean iPolymorphic) { + return internal.browseClass(iClassName, iPolymorphic); + } + + @Override + public void freeze() { + internal.freeze(); + } + + @Override + public void release() { + internal.release(); + } + + @Override + public void freeze(boolean throwException) { + internal.freeze(throwException); + } + + @Override + public ODocument newInstance() { + return internal.newInstance(); + } + + @Override + public ODictionary getDictionary() { + return internal.getDictionary(); + } + + @Override + public OSecurityUser getUser() { + return internal.getUser(); + } + + @Override + public RET load(ORecord iObject) { + return internal.load(iObject); + } + + @Override + public RET load(ORecord iObject, String iFetchPlan) { + return internal.load(iObject, iFetchPlan); + } + + @Override + public RET load(ORecord iObject, String iFetchPlan, boolean iIgnoreCache, boolean loadTombstone, + OStorage.LOCKING_STRATEGY iLockingStrategy) { + return internal.load(iObject, iFetchPlan, iIgnoreCache, loadTombstone, iLockingStrategy); + } + + @Override + public RET load(ORecord iObject, String iFetchPlan, boolean iIgnoreCache, boolean iUpdateCache, + boolean loadTombstone, OStorage.LOCKING_STRATEGY iLockingStrategy) { + return internal.load(iObject, iFetchPlan, iIgnoreCache, iUpdateCache, loadTombstone, iLockingStrategy); + } + + @Override + public RET load(ORecord iObject, String iFetchPlan, boolean iIgnoreCache) { + return internal.load(iObject, iFetchPlan, iIgnoreCache); + } + + @Override + public RET reload(ORecord iObject, String iFetchPlan, boolean iIgnoreCache) { + return internal.reload(iObject, iFetchPlan, iIgnoreCache); + } + + @Override + public RET reload(ORecord iObject, String iFetchPlan, boolean iIgnoreCache, boolean force) { + return internal.reload(iObject, iFetchPlan, iIgnoreCache, force); + } + + @Override + public RET load(ORID recordId) { + return internal.load(recordId); + } + + @Override + public RET load(ORID iRecordId, String iFetchPlan) { + return internal.load(iRecordId, iFetchPlan); + } + + @Override + public RET load(ORID iRecordId, String iFetchPlan, boolean iIgnoreCache) { + return internal.load(iRecordId, iFetchPlan, iIgnoreCache); + } + + @Override + public RET load(ORID iRecordId, String iFetchPlan, boolean iIgnoreCache, boolean loadTombstone, + OStorage.LOCKING_STRATEGY iLockingStrategy) { + return internal.load(iRecordId, iFetchPlan, iIgnoreCache, loadTombstone, iLockingStrategy); + } + + @Override + public RET load(ORID iRecordId, String iFetchPlan, boolean iIgnoreCache, boolean iUpdateCache, + boolean loadTombstone, OStorage.LOCKING_STRATEGY iLockingStrategy) { + return internal.load(iRecordId, iFetchPlan, iIgnoreCache, iUpdateCache, loadTombstone, iLockingStrategy); + } + + @Override + public RET save(ORecord iObject) { + return internal.save(iObject); + } + + @Override + public RET save(ORecord iObject, OPERATION_MODE iMode, boolean iForceCreate, + ORecordCallback iRecordCreatedCallback, ORecordCallback iRecordUpdatedCallback) { + return internal.save(iObject, iMode, iForceCreate, iRecordCreatedCallback, iRecordUpdatedCallback); + } + + @Override + public RET save(ORecord iObject, String iClusterName) { + return internal.save(iObject, iClusterName); + } + + @Override + public RET save(ORecord iObject, String iClusterName, OPERATION_MODE iMode, boolean iForceCreate, + ORecordCallback iRecordCreatedCallback, ORecordCallback iRecordUpdatedCallback) { + return internal.save(iObject, iClusterName, iMode, iForceCreate, iRecordCreatedCallback, iRecordUpdatedCallback); + } + + @Override + public ODatabase delete(ORecord iObject) { + internal.delete(iObject); + return this; + } + + @Override + public ODatabase delete(ORID iRID) { + internal.delete(iRID); + return this; + } + + @Override + public ODatabase delete(ORID iRID, int iVersion) { + internal.delete(iRID, iVersion); + return this; + } + + @Override + public boolean hide(ORID rid) { + return internal.hide(rid); + } + + @Override + public ODatabase cleanOutRecord(ORID rid, int version) { + internal.cleanOutRecord(rid, version); + return this; + } + + @Override + public OTransaction getTransaction() { + return internal.getTransaction(); + } + + @Override + public ODatabase begin() { + internal.begin(); + return this; + } + + @Override + public ODatabase begin(OTransaction.TXTYPE iStatus) { + internal.begin(iStatus); + return this; + } + + @Override + public ODatabase begin(OTransaction iTx) throws OTransactionException { + internal.begin(iTx); + return this; + } + + @Override + public ODatabase commit() throws OTransactionException { + internal.commit(); + return this; + } + + @Override + public ODatabase commit(boolean force) throws OTransactionException { + internal.commit(force); + return this; + } + + @Override + public ODatabase rollback() throws OTransactionException { + internal.rollback(); + return this; + } + + @Override + public ODatabase rollback(boolean force) throws OTransactionException { + internal.rollback(force); + return this; + } + + @Override + public > RET query(OQuery iCommand, Object... iArgs) { + return internal.query(iCommand, iArgs); + } + + @Override + public RET command(OCommandRequest iCommand) { + return internal.command(iCommand); + } + + @Override + public ORecordIteratorCluster browseCluster(String iClusterName) { + return internal.browseCluster(iClusterName); + } + + @Override + public ORecordIteratorCluster browseCluster(String iClusterName, long startClusterPosition, long endClusterPosition, + boolean loadTombstones) { + return internal.browseCluster(iClusterName, startClusterPosition, endClusterPosition, loadTombstones); + } + + @Override + public ORecordIteratorCluster browseCluster(String iClusterName, Class iRecordClass) { + return internal.browseCluster(iClusterName, iRecordClass); + } + + @Override + public ORecordIteratorCluster browseCluster(String iClusterName, Class iRecordClass, + long startClusterPosition, long endClusterPosition) { + return internal.browseCluster(iClusterName, iRecordClass, startClusterPosition, endClusterPosition); + } + + @Override + public ORecordIteratorCluster browseCluster(String iClusterName, Class iRecordClass, + long startClusterPosition, long endClusterPosition, boolean loadTombstones) { + return internal.browseCluster(iClusterName, iRecordClass, startClusterPosition, endClusterPosition, loadTombstones); + } + + @Override + public RET getRecord(OIdentifiable iIdentifiable) { + return internal.getRecord(iIdentifiable); + } + + @Override + public byte getRecordType() { + return internal.getRecordType(); + } + + @Override + public boolean isRetainRecords() { + return internal.isRetainRecords(); + } + + @Override + public ODatabaseDocument setRetainRecords(boolean iValue) { + return internal.setRetainRecords(iValue); + } + + @Override + public DB checkSecurity(ORule.ResourceGeneric resourceGeneric, String resourceSpecific, + int iOperation) { + internal.checkSecurity(resourceGeneric, resourceSpecific, iOperation); + return (DB) this; + } + + @Override + public DB checkSecurity(ORule.ResourceGeneric iResourceGeneric, int iOperation, + Object iResourceSpecific) { + internal.checkSecurity(iResourceGeneric, iOperation, iResourceSpecific); + return (DB) this; + } + + @Override + public DB checkSecurity(ORule.ResourceGeneric iResourceGeneric, int iOperation, + Object... iResourcesSpecific) { + internal.checkSecurity(iResourceGeneric, iOperation, iResourcesSpecific); + return (DB) this; + } + + @Override + public boolean isValidationEnabled() { + return internal.isValidationEnabled(); + } + + @Override + public DB setValidationEnabled(boolean iEnabled) { + internal.setValidationEnabled(iEnabled); + return (DB) this; + } + + @Override + public DB checkSecurity(String iResource, int iOperation) { + internal.checkSecurity(iResource, iOperation); + return (DB) this; + } + + @Override + public DB checkSecurity(String iResourceGeneric, int iOperation, Object iResourceSpecific) { + internal.checkSecurity(iResourceGeneric, iOperation, iResourceSpecific); + return (DB) this; + } + + @Override + public DB checkSecurity(String iResourceGeneric, int iOperation, Object... iResourcesSpecific) { + internal.checkSecurity(iResourceGeneric, iOperation, iResourcesSpecific); + return (DB) this; + } + + @Override + public boolean isPooled() { + return internal.isPooled(); + } + + @Override + public DB open(String iUserName, String iUserPassword) { + if ("remote".equals(type)) { + synchronized (remote) { + factory = remote.get(baseUrl); + if (factory == null) { + factory = OrientDBFactory.fromUrl("remote:" + baseUrl, null); + remote.put(baseUrl, factory); + } + } + internal = (ODatabaseDocumentInternal) factory.open(dbName, iUserName, iUserPassword); + } else { + synchronized (embedded) { + factory = embedded.get(baseUrl); + if (factory == null) { + factory = OrientDBFactory.fromUrl("embedded:" + baseUrl, null); + embedded.put(baseUrl, factory); + } + } + internal = (ODatabaseDocumentInternal) factory.open(dbName, iUserName, iUserPassword); + } + return (DB) this; + } + + @Override + public DB create() { + //TODO + return create((Map) null); + } + + @Override + public DB create(String incrementalBackupPath) { + return null; + } + + @Override + public DB create(Map iInitialSettings) { + if ("remote".equals(type)) { + throw new UnsupportedOperationException(); + } else if ("memory".equals(type)) { + synchronized (embedded) { + factory = embedded.get(baseUrl); + if (factory == null) { + factory = OrientDBFactory.fromUrl("embedded:" + baseUrl, null); + embedded.put(baseUrl, factory); + } + } + factory.create(dbName, null, null, OrientDBFactory.DatabaseType.MEMORY); + internal = (ODatabaseDocumentInternal) factory.open(dbName, "admin", "admin"); + + } else { + synchronized (embedded) { + factory = embedded.get(baseUrl); + if (factory == null) { + factory = OrientDBFactory.fromUrl("embedded:" + baseUrl, null); + embedded.put(baseUrl, factory); + } + } + factory.create(dbName, null, null, OrientDBFactory.DatabaseType.PLOCAL); + internal = (ODatabaseDocumentInternal) factory.open(dbName, "admin", "admin"); + } + return (DB) this; + + } + + @Override + public ODatabase activateOnCurrentThread() { + if (internal != null) + internal.activateOnCurrentThread(); + return this; + } + + @Override + public boolean isActiveOnCurrentThread() { + return internal.isActiveOnCurrentThread(); + } + + @Override + public void reload() { + internal.reload(); + } + + @Override + public void drop() { + //TODO + factory.drop(this.getName(), null, null); + } + + @Override + public OContextConfiguration getConfiguration() { + return internal.getConfiguration(); + } + + @Override + public boolean declareIntent(OIntent iIntent) { + return internal.declareIntent(iIntent); + } + + @Override + public boolean exists() { + if ("remote".equals(type)) { + throw new UnsupportedOperationException(); + } else { + synchronized (embedded) { + factory = embedded.get(baseUrl); + if (factory == null) { + factory = OrientDBFactory.fromUrl("embedded:" + baseUrl, null); + embedded.put(baseUrl, factory); + } + } + return factory.exists(dbName, null, null); + } + } + + @Override + public void close() { + //TODO + } + + @Override + public STATUS getStatus() { + return internal.getStatus(); + } + + @Override + public DB setStatus(STATUS iStatus) { + internal.setStatus(iStatus); + return (DB) this; + } + + @Override + public long getSize() { + return internal.getSize(); + } + + @Override + public String getName() { + return internal.getName(); + } + + @Override + public String getURL() { + return internal.getURL(); + } + + @Override + public OLocalRecordCache getLocalCache() { + return internal.getLocalCache(); + } + + @Override + public int getDefaultClusterId() { + return internal.getDefaultClusterId(); + } + + @Override + public int getClusters() { + return internal.getClusters(); + } + + @Override + public boolean existsCluster(String iClusterName) { + return internal.existsCluster(iClusterName); + } + + @Override + public Collection getClusterNames() { + return internal.getClusterNames(); + } + + @Override + public int getClusterIdByName(String iClusterName) { + return internal.getClusterIdByName(iClusterName); + } + + @Override + public String getClusterNameById(int iClusterId) { + return internal.getClusterNameById(iClusterId); + } + + @Override + public long getClusterRecordSizeByName(String iClusterName) { + return internal.getClusterRecordSizeByName(iClusterName); + } + + @Override + public long getClusterRecordSizeById(int iClusterId) { + return internal.getClusterRecordSizeById(iClusterId); + } + + @Override + public boolean isClosed() { + return internal == null || internal.isClosed(); + } + + @Override + public void truncateCluster(String clusterName) { + internal.truncateCluster(clusterName); + } + + @Override + public long countClusterElements(int iCurrentClusterId) { + return internal.countClusterElements(iCurrentClusterId); + } + + @Override + public long countClusterElements(int iCurrentClusterId, boolean countTombstones) { + return internal.countClusterElements(iCurrentClusterId, countTombstones); + } + + @Override + public long countClusterElements(int[] iClusterIds) { + return internal.countClusterElements(iClusterIds); + } + + @Override + public long countClusterElements(int[] iClusterIds, boolean countTombstones) { + return internal.countClusterElements(iClusterIds, countTombstones); + } + + @Override + public long countClusterElements(String iClusterName) { + return internal.countClusterElements(iClusterName); + } + + @Override + public int addCluster(String iClusterName, Object... iParameters) { + return internal.addCluster(iClusterName, iParameters); + } + + @Override + public int addBlobCluster(String iClusterName, Object... iParameters) { + return internal.addBlobCluster(iClusterName, iParameters); + } + + @Override + public Set getBlobClusterIds() { + return internal.getBlobClusterIds(); + } + + @Override + public int addCluster(String iClusterName, int iRequestedId, Object... iParameters) { + return internal.addCluster(iClusterName, iRequestedId, iParameters); + } + + @Override + public boolean dropCluster(String iClusterName, boolean iTruncate) { + return internal.dropCluster(iClusterName, iTruncate); + } + + @Override + public boolean dropCluster(int iClusterId, boolean iTruncate) { + return internal.dropCluster(iClusterId, iTruncate); + } + + @Override + public Object setProperty(String iName, Object iValue) { + return internal.setProperty(iName, iValue); + } + + @Override + public Object getProperty(String iName) { + return internal.getProperty(iName); + } + + @Override + public Iterator> getProperties() { + return internal.getProperties(); + } + + @Override + public Object get(ATTRIBUTES iAttribute) { + return internal.get(iAttribute); + } + + @Override + public DB set(ATTRIBUTES iAttribute, Object iValue) { + internal.set(iAttribute, iValue); + return (DB) this; + } + + @Override + public void registerListener(ODatabaseListener iListener) { + internal.registerListener(iListener); + } + + @Override + public void unregisterListener(ODatabaseListener iListener) { + internal.unregisterListener(iListener); + } + + @Override + public ORecordMetadata getRecordMetadata(ORID rid) { + return internal.getRecordMetadata(rid); + } + + @Override + public ODocument newInstance(String iClassName) { + return internal.newInstance(iClassName); + } + + @Override + public long countClass(String iClassName) { + return internal.countClass(iClassName); + } + + @Override + public long countClass(String iClassName, boolean iPolymorphic) { + return internal.countClass(iClassName, iPolymorphic); + } + + @Override + public List backup(OutputStream out, Map options, Callable callable, + OCommandOutputListener iListener, int compressionLevel, int bufferSize) throws IOException { + return internal.backup(out, options, callable, iListener, compressionLevel, bufferSize); + } + + @Override + public void restore(InputStream in, Map options, Callable callable, OCommandOutputListener iListener) + throws IOException { + internal.restore(in, options, callable, iListener); + } + + public void setSerializer(ORecordSerializer serializer) { + ((ODatabaseDocumentTxOrig) internal).setSerializer(serializer); + } + + @Override + public OTodoResultSet query(String query, Object... args) { + return internal.query(query, args); + } + + @Override + public OTodoResultSet query(String query, Map args) throws OCommandSQLParsingException, OCommandExecutionException { + return internal.query(query, args); + } + + @Override + public OTodoResultSet command(String query, Map args) throws OCommandSQLParsingException, OCommandExecutionException { + return internal.command(query, args); + } + + @Override + public OTodoResultSet command(String query, Object... args) throws OCommandSQLParsingException, OCommandExecutionException { + return internal.command(query, args); + } + + @Override + public DB setCustom(String name, Object iValue) { + return internal.setCustom(name, iValue); + } + + @Override + public boolean isPrefetchRecords() { + return internal.isPrefetchRecords(); + } + + public void setPrefetchRecords(boolean prefetchRecords) { + internal.setPrefetchRecords(prefetchRecords); + }; + +} diff --git a/core/src/main/java/com/orientechnologies/orient/core/db/document/ODatabaseDocumentTxInternal.java b/core/src/main/java/com/orientechnologies/orient/core/db/document/ODatabaseDocumentTxInternal.java index 44e7e8f4c13..3d8c754ab2c 100644 --- a/core/src/main/java/com/orientechnologies/orient/core/db/document/ODatabaseDocumentTxInternal.java +++ b/core/src/main/java/com/orientechnologies/orient/core/db/document/ODatabaseDocumentTxInternal.java @@ -12,11 +12,11 @@ private ODatabaseDocumentTxInternal() { } public static ODatabaseSessionMetadata getSessionMetadata(ODatabaseDocument db) { - return ((ODatabaseDocumentTx)db).sessionMetadata; + return ((ODatabaseDocumentTxOrig)db).sessionMetadata; } public static void setSessionMetadata(ODatabaseDocument db, ODatabaseSessionMetadata sessionMetadata) { - ((ODatabaseDocumentTx)db).sessionMetadata = sessionMetadata; + ((ODatabaseDocumentTxOrig)db).sessionMetadata = sessionMetadata; } } diff --git a/core/src/main/java/com/orientechnologies/orient/core/db/document/ODatabaseDocumentTxOrig.java b/core/src/main/java/com/orientechnologies/orient/core/db/document/ODatabaseDocumentTxOrig.java new file mode 100755 index 00000000000..2b78e294ca9 --- /dev/null +++ b/core/src/main/java/com/orientechnologies/orient/core/db/document/ODatabaseDocumentTxOrig.java @@ -0,0 +1,3532 @@ +/* + * + * * Copyright 2014 Orient Technologies LTD (info(at)orientechnologies.com) + * * + * * Licensed under the Apache License, Version 2.0 (the "License"); + * * you may not use this file except in compliance with the License. + * * You may obtain a copy of the License at + * * + * * http://www.apache.org/licenses/LICENSE-2.0 + * * + * * Unless required by applicable law or agreed to in writing, software + * * distributed under the License is distributed on an "AS IS" BASIS, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * See the License for the specific language governing permissions and + * * limitations under the License. + * * + * * For more information: http://www.orientechnologies.com + * + */ + +package com.orientechnologies.orient.core.db.document; + +import com.orientechnologies.common.concur.ONeedRetryException; +import com.orientechnologies.common.exception.OException; +import com.orientechnologies.common.io.OIOUtils; +import com.orientechnologies.common.listener.OListenerManger; +import com.orientechnologies.common.log.OLogManager; +import com.orientechnologies.common.util.OCallable; +import com.orientechnologies.common.util.OCommonConst; +import com.orientechnologies.common.util.OPair; +import com.orientechnologies.orient.core.OUncompletedCommit; +import com.orientechnologies.orient.core.Orient; +import com.orientechnologies.orient.core.cache.OCommandCacheHook; +import com.orientechnologies.orient.core.cache.OLocalRecordCache; +import com.orientechnologies.orient.core.command.OCommandOutputListener; +import com.orientechnologies.orient.core.command.OCommandRequest; +import com.orientechnologies.orient.core.command.OCommandRequestInternal; +import com.orientechnologies.orient.core.config.OContextConfiguration; +import com.orientechnologies.orient.core.config.OGlobalConfiguration; +import com.orientechnologies.orient.core.conflict.ORecordConflictStrategy; +import com.orientechnologies.orient.core.db.*; +import com.orientechnologies.orient.core.db.record.*; +import com.orientechnologies.orient.core.db.record.ridbag.sbtree.ORidBagDeleter; +import com.orientechnologies.orient.core.db.record.ridbag.sbtree.OSBTreeCollectionManager; +import com.orientechnologies.orient.core.dictionary.ODictionary; +import com.orientechnologies.orient.core.exception.*; +import com.orientechnologies.orient.core.fetch.OFetchHelper; +import com.orientechnologies.orient.core.hook.ORecordHook; +import com.orientechnologies.orient.core.id.ORID; +import com.orientechnologies.orient.core.id.ORecordId; +import com.orientechnologies.orient.core.index.ClassIndexManagerRemote; +import com.orientechnologies.orient.core.index.OClassIndexManager; +import com.orientechnologies.orient.core.intent.OIntent; +import com.orientechnologies.orient.core.iterator.ORecordIteratorClass; +import com.orientechnologies.orient.core.iterator.ORecordIteratorCluster; +import com.orientechnologies.orient.core.metadata.OMetadata; +import com.orientechnologies.orient.core.metadata.OMetadataDefault; +import com.orientechnologies.orient.core.metadata.function.OFunctionTrigger; +import com.orientechnologies.orient.core.metadata.schema.OClass; +import com.orientechnologies.orient.core.metadata.schema.OSchemaProxy; +import com.orientechnologies.orient.core.metadata.security.*; +import com.orientechnologies.orient.core.metadata.sequence.OSequenceTrigger; +import com.orientechnologies.orient.core.query.OQuery; +import com.orientechnologies.orient.core.query.live.OLiveQueryHook; +import com.orientechnologies.orient.core.record.ORecord; +import com.orientechnologies.orient.core.record.ORecordInternal; +import com.orientechnologies.orient.core.record.ORecordVersionHelper; +import com.orientechnologies.orient.core.record.impl.OBlob; +import com.orientechnologies.orient.core.record.impl.ODirtyManager; +import com.orientechnologies.orient.core.record.impl.ODocument; +import com.orientechnologies.orient.core.record.impl.ODocumentInternal; +import com.orientechnologies.orient.core.schedule.OSchedulerTrigger; +import com.orientechnologies.orient.core.serialization.serializer.binary.OBinarySerializerFactory; +import com.orientechnologies.orient.core.serialization.serializer.record.ORecordSaveThreadLocal; +import com.orientechnologies.orient.core.serialization.serializer.record.ORecordSerializer; +import com.orientechnologies.orient.core.serialization.serializer.record.ORecordSerializerFactory; +import com.orientechnologies.orient.core.serialization.serializer.record.string.ORecordSerializerSchemaAware2CSV; +import com.orientechnologies.orient.core.sql.OCommandSQL; +import com.orientechnologies.orient.core.sql.executor.OTodoResultSet; +import com.orientechnologies.orient.core.sql.parser.OStatement; +import com.orientechnologies.orient.core.storage.*; +import com.orientechnologies.orient.core.storage.impl.local.OAbstractPaginatedStorage; +import com.orientechnologies.orient.core.storage.impl.local.OFreezableStorageComponent; +import com.orientechnologies.orient.core.storage.impl.local.paginated.OOfflineClusterException; +import com.orientechnologies.orient.core.storage.impl.local.paginated.ORecordSerializationContext; +import com.orientechnologies.orient.core.tx.OTransaction; +import com.orientechnologies.orient.core.tx.OTransactionNoTx; +import com.orientechnologies.orient.core.tx.OTransactionOptimistic; +import com.orientechnologies.orient.core.tx.OTransactionRealAbstract; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.concurrent.Callable; +import java.util.concurrent.atomic.AtomicReference; + +/** + * Document API entrypoint. + * + * @author Luca Garulli + */ +@SuppressWarnings("unchecked") +public class ODatabaseDocumentTxOrig extends OListenerManger implements ODatabaseDocumentInternal { + + protected final Map properties = new HashMap(); + protected Map unmodifiableHooks; + protected final Set inHook = new HashSet(); + protected ORecordSerializer serializer; + protected String url; + protected OStorage storage; + protected STATUS status; + protected OIntent currentIntent; + protected ODatabaseInternal databaseOwner; + protected OMetadataDefault metadata; + protected OImmutableUser user; + protected byte recordType; + protected final Map hooks = new LinkedHashMap(); + protected boolean retainRecords = true; + protected OLocalRecordCache localCache; + protected boolean mvcc; + protected OCurrentStorageComponentsFactory componentsFactory; + protected boolean initialized = false; + protected OTransaction currentTx; + protected boolean keepStorageOpen = false; + protected final AtomicReference owner = new AtomicReference(); + protected boolean ownerProtection = true; + + protected ODatabaseSessionMetadata sessionMetadata; + + protected final ORecordHook[][] hooksByScope = new ORecordHook[ORecordHook.SCOPE.values().length][]; + protected OSharedContext sharedContext; + + private boolean prefetchRecords; + + protected ODatabaseDocumentTxOrig() { + //DO NOTHING IS FOR EXTENDED OBJECTS + super(false); + } + + /** + * Creates a new connection to the database. + * + * @param iURL of the database + */ + public ODatabaseDocumentTxOrig(final String iURL) { + this(iURL, false, true); + } + + public ODatabaseDocumentTxOrig(final String iURL, boolean keepStorageOpen, boolean ownerProtection) { + super(false); + + this.ownerProtection = ownerProtection; + + if (iURL == null) + throw new IllegalArgumentException("URL parameter is null"); + + activateOnCurrentThread(); + + try { + this.keepStorageOpen = keepStorageOpen; + url = iURL.replace('\\', '/'); + status = STATUS.CLOSED; + + // SET DEFAULT PROPERTIES + setProperty("fetch-max", 50); + + storage = Orient.instance().loadStorage(url); + + // OVERWRITE THE URL + url = storage.getURL(); + + unmodifiableHooks = Collections.unmodifiableMap(hooks); + + recordType = ODocument.RECORD_TYPE; + localCache = new OLocalRecordCache(); + + mvcc = OGlobalConfiguration.DB_MVCC.getValueAsBoolean(); + + init(); + + databaseOwner = this; + } catch (Exception t) { + if (storage != null) + Orient.instance().unregisterStorage(storage); + ODatabaseRecordThreadLocal.INSTANCE.remove(); + + throw OException.wrapException(new ODatabaseException("Error on opening database '" + iURL + "'"), t); + } + + setSerializer(getDefaultSerializer()); + } + + /** + * @return default serializer which is used to serialize documents. Default serializer is common for all database instances. + */ + public static ORecordSerializer getDefaultSerializer() { + return ORecordSerializerFactory.instance().getDefaultRecordSerializer(); + } + + /** + * Sets default serializer. The default serializer is common for all database instances. + * + * @param iDefaultSerializer new default serializer value + */ + public static void setDefaultSerializer(ORecordSerializer iDefaultSerializer) { + ORecordSerializerFactory.instance().setDefaultRecordSerializer(iDefaultSerializer); + } + + /** + * Opens connection to the storage with given user and password. + *

+ * But we do suggest {@link com.orientechnologies.orient.core.db.OPartitionedDatabasePool#acquire()} instead. It will make work + * faster even with embedded database. + * + * @param iUserName Username to login + * @param iUserPassword Password associated to the user + * @return Current database instance. + */ + @Override + public DB open(final String iUserName, final String iUserPassword) { + boolean failure = true; + setupThreadOwner(); + activateOnCurrentThread(); + try { + if (status == STATUS.OPEN) + throw new IllegalStateException("Database " + getName() + " is already open"); + + if (user != null && !user.getName().equals(iUserName)) + initialized = false; + + final String encKey = (String) getProperty(OGlobalConfiguration.STORAGE_ENCRYPTION_KEY.getKey()); + String currKey = null; + + if (storage.getConfiguration() != null && storage.getConfiguration().getContextConfiguration() != null) { + currKey = (String) storage.getConfiguration().getContextConfiguration() + .getValue(OGlobalConfiguration.STORAGE_ENCRYPTION_KEY); + + // If an encryption key is set as a database property, and + // the storage engine is open and has an encryption key value, and + // the two encryption keys differ, force the storage closed so that the + // new encryption key in properties will be used. + if (encKey != null && currKey != null && !encKey.equals(currKey)) { + // If the storage is open... + if (!storage.isClosed()) { + storage.close(true, false); // force it closed + } + } + } + + if (storage.isClosed()) { + OContextConfiguration conf = new OContextConfiguration(); + bindPropertiesToContext(conf, properties); + storage.open(iUserName, iUserPassword, conf); + initialized = false; + } else if (storage instanceof OStorageProxy) { + final String name = ((OStorageProxy) storage).getUserName(); + if (name == null || !name.equals(iUserName)) { + storage.close(); + OContextConfiguration conf = new OContextConfiguration(); + bindPropertiesToContext(conf, properties); + storage.open(iUserName, iUserPassword, conf); + } + } + + status = STATUS.OPEN; + + initAtFirstOpen(iUserName, iUserPassword); + + if (!(getStorage() instanceof OStorageProxy)) { + final OSecurity security = metadata.getSecurity(); + if (user == null || user.getVersion() != security.getVersion() || !user.getName().equalsIgnoreCase(iUserName)) { + final OUser usr = metadata.getSecurity().authenticate(iUserName, iUserPassword); + if (usr != null) + user = new OImmutableUser(security.getVersion(), usr); + else + user = null; + + checkSecurity(ORule.ResourceGeneric.DATABASE, ORole.PERMISSION_READ); + } + } + + // WAKE UP LISTENERS + callOnOpenListeners(); + + failure = false; + } catch (OException e) { + close(); + throw e; + } catch (Exception e) { + close(); + throw OException.wrapException(new ODatabaseException("Cannot open database url=" + getURL()), e); + } finally { + if (failure && ownerProtection) + owner.set(null); + } + return (DB) this; + } + + /** + * Opens a database using an authentication token received as an argument. + * + * @param iToken Authentication token + * @return The Database instance itself giving a "fluent interface". Useful to call multiple methods in chain. + */ + public DB open(final OToken iToken) { + boolean failure = true; + + setupThreadOwner(); + activateOnCurrentThread(); + + try { + if (status == STATUS.OPEN) + throw new IllegalStateException("Database " + getName() + " is already open"); + + if (user != null && !user.getIdentity().equals(iToken.getUserId())) + initialized = false; + + if (storage instanceof OStorageProxy) { + throw new ODatabaseException("Cannot use a token open on remote database"); + } + if (storage.isClosed()) { + // i don't have username and password at this level, anyway the storage embedded don't really need it + OContextConfiguration conf = new OContextConfiguration(); + bindPropertiesToContext(conf, properties); + storage.open("", "", conf); + } + + status = STATUS.OPEN; + + initAtFirstOpen(null, null); + + final OSecurity security = metadata.getSecurity(); + if (user == null || user.getVersion() != security.getVersion()) { + final OUser usr = metadata.getSecurity().authenticate(iToken); + if (usr != null) + user = new OImmutableUser(security.getVersion(), usr); + else + user = null; + + checkSecurity(ORule.ResourceGeneric.DATABASE, ORole.PERMISSION_READ); + } + + // WAKE UP LISTENERS + callOnOpenListeners(); + + failure = false; + } catch (OException e) { + close(); + throw e; + } catch (Exception e) { + close(); + throw OException.wrapException(new ODatabaseException("Cannot open database"), e); + } finally { + if (failure && ownerProtection) { + owner.set(null); + } + + } + return (DB) this; + } + + protected void setupThreadOwner() { + if (!ownerProtection) + return; + + final Thread current = Thread.currentThread(); + final Thread o = owner.get(); + + if (o != null || !owner.compareAndSet(null, current)) { + throw new IllegalStateException("Current instance is owned by other thread" + (o != null ? " : '" + o.getName() + "'" : "")); + } + } + + public void callOnOpenListeners() { + // WAKE UP DB LIFECYCLE LISTENER + for (Iterator it = Orient.instance().getDbLifecycleListeners(); it.hasNext(); ) + it.next().onOpen(getDatabaseOwner()); + + // WAKE UP LISTENERS + for (ODatabaseListener listener : getListenersCopy()) + try { + listener.onOpen(getDatabaseOwner()); + } catch (Throwable t) { + t.printStackTrace(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public DB create() { + return create((Map) null); + } + + /** + * {@inheritDoc} + */ + @Override + public DB create(String incrementalBackupPath) { + create(); + + final OStorage storage = getStorage(); + storage.restoreFromIncrementalBackup(incrementalBackupPath); + loadMetadata(); + + return (DB) this; + } + + protected void loadMetadata() { + metadata = new OMetadataDefault(this); + OSharedContext shared = getStorage().getResource(OSharedContext.class.getName(), new Callable() { + @Override + public OSharedContext call() throws Exception { + OSharedContext shared = new OSharedContext(getStorage()); + return shared; + } + }); + metadata.init(shared); + shared.load(this); + } + + @Override + public DB create(final Map iInitialSettings) { + setupThreadOwner(); + activateOnCurrentThread(); + + try { + if (status == STATUS.OPEN) + throw new IllegalStateException("Database " + getName() + " is already open"); + + if (storage == null) + storage = Orient.instance().loadStorage(url); + OContextConfiguration config = new OContextConfiguration(); + bindPropertiesToContextGlobal(config, iInitialSettings); + bindPropertiesToContext(config, properties); + storage.create(config); + + status = STATUS.OPEN; + + componentsFactory = getStorage().getComponentsFactory(); + + localCache.startup(); + + getStorage().getConfiguration().setRecordSerializer(getSerializer().toString()); + getStorage().getConfiguration().setRecordSerializerVersion(getSerializer().getCurrentVersion()); + + // since 2.1 newly created databases use strict SQL validation by default + getStorage().getConfiguration().setProperty(OStatement.CUSTOM_STRICT_SQL, "true"); + + getStorage().getConfiguration().update(); + + // THIS IF SHOULDN'T BE NEEDED, CREATE HAPPEN ONLY IN EMBEDDED + if (!(getStorage() instanceof OStorageProxy)) + installHooksEmbedded(); + + metadata = new OMetadataDefault(this); + // CREATE THE DEFAULT SCHEMA WITH DEFAULT USER + OSharedContext shared = getStorage().getResource(OSharedContext.class.getName(), new Callable() { + @Override + public OSharedContext call() throws Exception { + OSharedContext shared = new OSharedContext(getStorage()); + return shared; + } + }); + metadata.init(shared); + shared.create(this); + + if (!(getStorage() instanceof OStorageProxy)) + registerHook(new OCommandCacheHook(this), ORecordHook.HOOK_POSITION.REGULAR); + + registerHook(new OSecurityTrackerHook(metadata.getSecurity(), this), ORecordHook.HOOK_POSITION.LAST); + + final OUser usr = getMetadata().getSecurity().getUser(OUser.ADMIN); + + if (usr == null) + user = null; + else + user = new OImmutableUser(getMetadata().getSecurity().getVersion(), usr); + + // WAKE UP DB LIFECYCLE LISTENER + for (Iterator it = Orient.instance().getDbLifecycleListeners(); it.hasNext(); ) + it.next().onCreate(getDatabaseOwner()); + + // WAKE UP LISTENERS + for (ODatabaseListener listener : browseListeners()) + try { + listener.onCreate(this); + } catch (Throwable ignore) { + } + } catch (Exception e) { + // REMOVE THE (PARTIAL) DATABASE + try { + drop(); + } catch (Exception ex) { + // IGNORE IT + } + + // DELETE THE STORAGE TOO + try { + if (storage == null) + storage = Orient.instance().loadStorage(url); + storage.delete(); + } catch (Exception ex) { + // IGNORE IT + } + + status = STATUS.CLOSED; + owner.set(null); + + throw OException.wrapException(new ODatabaseException("Cannot create database '" + getName() + "'"), e); + } + return (DB) this; + } + + /** + * {@inheritDoc} + */ + @Override + public void drop() { + checkOpeness(); + checkIfActive(); + + checkSecurity(ORule.ResourceGeneric.DATABASE, ORole.PERMISSION_DELETE); + + callOnDropListeners(); + + if (metadata != null) { + metadata.close(); + metadata = null; + } + + closeOnDelete(); + + try { + if (storage == null) + storage = Orient.instance().loadStorage(url); + + storage.delete(); + storage = null; + sharedContext = null; + status = STATUS.CLOSED; + ODatabaseRecordThreadLocal.INSTANCE.remove(); + clearOwner(); + + } catch (OException e) { + // PASS THROUGH + throw e; + } catch (Exception e) { + throw OException.wrapException(new ODatabaseException("Cannot delete database"), e); + } + } + + /** + * Returns a copy of current database if it's open. The returned instance can be used by another thread without affecting current + * instance. The database copy is not set in thread local. + */ + public ODatabaseDocumentInternal copy() { + ODatabaseDocumentInternal dbInThreadLocal = ODatabaseRecordThreadLocal.INSTANCE.getIfDefined(); + if (this.isClosed()) + throw new ODatabaseException("Cannot copy a closed db"); + + final ODatabaseDocumentTxOrig db = new ODatabaseDocumentTxOrig(this.url); + db.setupThreadOwner(); + + db.user = this.user; + db.properties.putAll(this.properties); + db.serializer = this.serializer; + + db.componentsFactory = this.componentsFactory; + + db.initialized = true; + if (storage instanceof OStorageProxy) { + db.storage = ((OStorageProxy) storage).copy(this, db); + ((OStorageProxy) db.storage).addUser(); + } else { + db.storage = storage; + } + + db.setStatus(STATUS.OPEN); + db.loadMetadata(); + + if (!(db.getStorage() instanceof OStorageProxy)) + db.installHooksEmbedded(); + else + db.installHooksRemote(); + + db.initialized = true; + + if (dbInThreadLocal != null) { + dbInThreadLocal.activateOnCurrentThread(); + } else { + if (ODatabaseRecordThreadLocal.INSTANCE.isDefined()) { + ODatabaseRecordThreadLocal.INSTANCE.remove(); + } + } + + return db; + } + + public void callOnCloseListeners() { + // WAKE UP DB LIFECYCLE LISTENER + for (Iterator it = Orient.instance().getDbLifecycleListeners(); it.hasNext(); ) + it.next().onClose(getDatabaseOwner()); + + // WAKE UP LISTENERS + for (ODatabaseListener listener : getListenersCopy()) + try { + listener.onClose(getDatabaseOwner()); + } catch (Throwable t) { + t.printStackTrace(); + } + } + + public void callOnDropListeners() { + // WAKE UP DB LIFECYCLE LISTENER + for (Iterator it = Orient.instance().getDbLifecycleListeners(); it.hasNext(); ) { + activateOnCurrentThread(); + it.next().onDrop(getDatabaseOwner()); + } + + // WAKE UP LISTENERS + for (ODatabaseListener listener : getListenersCopy()) + try { + activateOnCurrentThread(); + listener.onDelete(getDatabaseOwner()); + } catch (Throwable t) { + t.printStackTrace(); + } + } + + /** + * {@inheritDoc} + */ + public RET getRecord(final OIdentifiable iIdentifiable) { + if (iIdentifiable instanceof ORecord) + return (RET) iIdentifiable; + return (RET) load(iIdentifiable.getIdentity()); + } + + @Override + public void reload() { + checkIfActive(); + + if (this.isClosed()) + throw new ODatabaseException("Cannot reload a closed db"); + + metadata.reload(); + storage.reload(); + } + + /** + * {@inheritDoc} + */ + public RET load(final ORID iRecordId, final String iFetchPlan, final boolean iIgnoreCache) { + return (RET) executeReadRecord((ORecordId) iRecordId, null, -1, iFetchPlan, iIgnoreCache, !iIgnoreCache, false, + OStorage.LOCKING_STRATEGY.DEFAULT, new SimpleRecordReader(prefetchRecords)); + } + + /** + * Deletes the record checking the version. + */ + public ODatabase delete(final ORID iRecord, final int iVersion) { + executeDeleteRecord(iRecord, iVersion, true, OPERATION_MODE.SYNCHRONOUS, false); + return this; + } + + public ODatabase cleanOutRecord(final ORID iRecord, final int iVersion) { + executeDeleteRecord(iRecord, iVersion, true, OPERATION_MODE.SYNCHRONOUS, true); + return this; + } + + public String getType() { + return TYPE; + } + + /** + * Deletes the record without checking the version. + */ + public ODatabaseDocument delete(final ORID iRecord, final OPERATION_MODE iMode) { + ORecord record = iRecord.getRecord(); + if (record == null) + return this; + + delete(record, iMode); + return this; + } + + public ODatabaseDocument delete(final ORecord iRecord, final OPERATION_MODE iMode) { + checkIfActive(); + currentTx.deleteRecord(iRecord, iMode); + return this; + } + + public ORecordIteratorCluster browseCluster(final String iClusterName, final Class iClass) { + checkSecurity(ORule.ResourceGeneric.CLUSTER, ORole.PERMISSION_READ, iClusterName); + + checkIfActive(); + + final int clusterId = getClusterIdByName(iClusterName); + + return new ORecordIteratorCluster(this, this, clusterId); + } + + /** + * {@inheritDoc} + */ + @Override + @Deprecated + public ORecordIteratorCluster browseCluster(final String iClusterName, final Class iRecordClass, + final long startClusterPosition, final long endClusterPosition, final boolean loadTombstones) { + checkSecurity(ORule.ResourceGeneric.CLUSTER, ORole.PERMISSION_READ, iClusterName); + checkIfActive(); + + final int clusterId = getClusterIdByName(iClusterName); + + return new ORecordIteratorCluster(this, this, clusterId, startClusterPosition, endClusterPosition, loadTombstones, + OStorage.LOCKING_STRATEGY.DEFAULT); + } + + @Override + public ORecordIteratorCluster browseCluster(String iClusterName, Class iRecordClass, + long startClusterPosition, long endClusterPosition) { + checkSecurity(ORule.ResourceGeneric.CLUSTER, ORole.PERMISSION_READ, iClusterName); + checkIfActive(); + + final int clusterId = getClusterIdByName(iClusterName); + + return new ORecordIteratorCluster(this, this, clusterId, startClusterPosition, endClusterPosition); + } + + /** + * {@inheritDoc} + */ + public OCommandRequest command(final OCommandRequest iCommand) { + checkSecurity(ORule.ResourceGeneric.COMMAND, ORole.PERMISSION_READ); + checkIfActive(); + + final OCommandRequestInternal command = (OCommandRequestInternal) iCommand; + + try { + command.reset(); + return command; + + } catch (Exception e) { + throw OException.wrapException(new ODatabaseException("Error on command execution"), e); + } + } + + /** + * {@inheritDoc} + */ + public > RET query(final OQuery iCommand, final Object... iArgs) { + checkIfActive(); + iCommand.reset(); + return (RET) iCommand.execute(iArgs); + } + + /** + * {@inheritDoc} + */ + public byte getRecordType() { + return recordType; + } + + /** + * {@inheritDoc} + */ + @Override + public long countClusterElements(final int[] iClusterIds) { + return countClusterElements(iClusterIds, false); + } + + /** + * {@inheritDoc} + */ + @Override + public long countClusterElements(final int iClusterId) { + return countClusterElements(iClusterId, false); + } + + /** + * {@inheritDoc} + */ + @Override + public void truncateCluster(String clusterName) { + command(new OCommandSQL("truncate cluster " + clusterName)).execute(); + } + + /** + * {@inheritDoc} + */ + @Override + public long countClusterElements(int iClusterId, boolean countTombstones) { + final String name = getClusterNameById(iClusterId); + if (name == null) + return 0; + + checkSecurity(ORule.ResourceGeneric.CLUSTER, ORole.PERMISSION_READ, name); + checkIfActive(); + + return storage.count(iClusterId, countTombstones); + } + + /** + * {@inheritDoc} + */ + @Override + public long countClusterElements(int[] iClusterIds, boolean countTombstones) { + checkIfActive(); + String name; + for (int iClusterId : iClusterIds) { + name = getClusterNameById(iClusterId); + checkSecurity(ORule.ResourceGeneric.CLUSTER, ORole.PERMISSION_READ, name); + } + + return storage.count(iClusterIds, countTombstones); + } + + /** + * {@inheritDoc} + */ + @Override + public long countClusterElements(final String iClusterName) { + checkSecurity(ORule.ResourceGeneric.CLUSTER, ORole.PERMISSION_READ, iClusterName); + checkIfActive(); + + final int clusterId = getClusterIdByName(iClusterName); + if (clusterId < 0) + throw new IllegalArgumentException("Cluster '" + iClusterName + "' was not found"); + return storage.count(clusterId); + } + + /** + * {@inheritDoc} + */ + public OMetadataDefault getMetadata() { + checkOpeness(); + return metadata; + } + + /** + * {@inheritDoc} + */ + public DB checkSecurity(final ORule.ResourceGeneric resourceGeneric, final String resourceSpecific, + final int iOperation) { + if (user != null) { + try { + user.allow(resourceGeneric, resourceSpecific, iOperation); + } catch (OSecurityAccessException e) { + + if (OLogManager.instance().isDebugEnabled()) + OLogManager.instance() + .debug(this, "User '%s' tried to access the reserved resource '%s.%s', operation '%s'", getUser(), resourceGeneric, + resourceSpecific, iOperation); + + throw e; + } + } + return (DB) this; + } + + /** + * {@inheritDoc} + */ + public DB checkSecurity(final ORule.ResourceGeneric iResourceGeneric, final int iOperation, + final Object... iResourcesSpecific) { + + if (user != null) { + try { + if (iResourcesSpecific.length != 0) { + for (Object target : iResourcesSpecific) { + if (target != null) { + user.allow(iResourceGeneric, target.toString(), iOperation); + } else + user.allow(iResourceGeneric, null, iOperation); + } + } else + user.allow(iResourceGeneric, null, iOperation); + } catch (OSecurityAccessException e) { + if (OLogManager.instance().isDebugEnabled()) + OLogManager.instance() + .debug(this, "[checkSecurity] User '%s' tried to access the reserved resource '%s', target(s) '%s', operation '%s'", + getUser(), iResourceGeneric, Arrays.toString(iResourcesSpecific), iOperation); + + throw e; + } + } + return (DB) this; + } + + /** + * {@inheritDoc} + */ + public DB checkSecurity(final ORule.ResourceGeneric iResourceGeneric, final int iOperation, + final Object iResourceSpecific) { + checkOpeness(); + if (user != null) { + try { + if (iResourceSpecific != null) + user.allow(iResourceGeneric, iResourceSpecific.toString(), iOperation); + else + user.allow(iResourceGeneric, null, iOperation); + } catch (OSecurityAccessException e) { + if (OLogManager.instance().isDebugEnabled()) + OLogManager.instance() + .debug(this, "[checkSecurity] User '%s' tried to access the reserved resource '%s', target '%s', operation '%s'", + getUser(), iResourceGeneric, iResourceSpecific, iOperation); + + throw e; + } + } + return (DB) this; + } + + /** + * {@inheritDoc} + */ + @Override + public ODatabaseInternal getDatabaseOwner() { + ODatabaseInternal current = databaseOwner; + + while (current != null && current != this && current.getDatabaseOwner() != current) + current = current.getDatabaseOwner(); + + return current; + } + + /** + * {@inheritDoc} + */ + @Override + public ODatabaseInternal setDatabaseOwner(ODatabaseInternal iOwner) { + databaseOwner = iOwner; + return this; + } + + /** + * {@inheritDoc} + */ + public boolean isRetainRecords() { + return retainRecords; + } + + /** + * {@inheritDoc} + */ + public ODatabaseDocument setRetainRecords(boolean retainRecords) { + this.retainRecords = retainRecords; + return this; + } + + /** + * {@inheritDoc} + */ + public DB setStatus(final STATUS status) { + checkIfActive(); + setStatusInternal(status); + return (DB) this; + } + + public void setStatusInternal(final STATUS status) { + this.status = status; + } + + /** + * Deprecated since v2.2 + */ + @Deprecated + public void setDefaultClusterIdInternal(final int iDefClusterId) { + checkIfActive(); + getStorage().setDefaultClusterId(iDefClusterId); + } + + /** + * {@inheritDoc} + */ + public void setInternal(final ATTRIBUTES iAttribute, final Object iValue) { + set(iAttribute, iValue); + } + + /** + * {@inheritDoc} + */ + public OSecurityUser getUser() { + return user; + } + + /** + * {@inheritDoc} + */ + public void setUser(final OSecurityUser user) { + checkIfActive(); + if (user instanceof OUser) { + OMetadata metadata = getMetadata(); + if (metadata != null) { + final OSecurity security = metadata.getSecurity(); + this.user = new OImmutableUser(security.getVersion(), (OUser) user); + } else + this.user = new OImmutableUser(-1, (OUser) user); + } else + this.user = (OImmutableUser) user; + } + + public void reloadUser() { + if (user != null) { + activateOnCurrentThread(); + + OMetadata metadata = getMetadata(); + + if (metadata != null) { + final OSecurity security = metadata.getSecurity(); + OUser secGetUser = security.getUser(user.getName()); + + if (secGetUser != null) + user = new OImmutableUser(security.getVersion(), secGetUser); + else + user = new OImmutableUser(-1, new OUser()); + } else + user = new OImmutableUser(-1, new OUser()); + } + } + + /** + * {@inheritDoc} + */ + public boolean isMVCC() { + return mvcc; + } + + /** + * {@inheritDoc} + */ + public > DB setMVCC(boolean mvcc) { + this.mvcc = mvcc; + return (DB) this; + } + + /** + * {@inheritDoc} + */ + public ODictionary getDictionary() { + checkOpeness(); + return metadata.getIndexManager().getDictionary(); + } + + /** + * {@inheritDoc} + */ + public > DB registerHook(final ORecordHook iHookImpl, final ORecordHook.HOOK_POSITION iPosition) { + checkOpeness(); + checkIfActive(); + + final Map tmp = new LinkedHashMap(hooks); + tmp.put(iHookImpl, iPosition); + hooks.clear(); + for (ORecordHook.HOOK_POSITION p : ORecordHook.HOOK_POSITION.values()) { + for (Map.Entry e : tmp.entrySet()) { + if (e.getValue() == p) + hooks.put(e.getKey(), e.getValue()); + } + } + + compileHooks(); + + return (DB) this; + } + + /** + * {@inheritDoc} + */ + public > DB registerHook(final ORecordHook iHookImpl) { + return (DB) registerHook(iHookImpl, ORecordHook.HOOK_POSITION.REGULAR); + } + + /** + * {@inheritDoc} + */ + public > DB unregisterHook(final ORecordHook iHookImpl) { + checkIfActive(); + if (iHookImpl != null) { + iHookImpl.onUnregister(); + hooks.remove(iHookImpl); + compileHooks(); + } + + return (DB) this; + } + + /** + * {@inheritDoc} + */ + @Override + public OLocalRecordCache getLocalCache() { + return localCache; + } + + /** + * {@inheritDoc} + */ + public Map getHooks() { + return unmodifiableHooks; + } + + /** + * Callback the registered hooks if any. + * + * @param type Hook type. Define when hook is called. + * @param id Record received in the callback + * @return True if the input record is changed, otherwise false + */ + public ORecordHook.RESULT callbackHooks(final ORecordHook.TYPE type, final OIdentifiable id) { + if (id == null || hooks.isEmpty() || id.getIdentity().getClusterId() == 0) + return ORecordHook.RESULT.RECORD_NOT_CHANGED; + + final ORecordHook.SCOPE scope = ORecordHook.SCOPE.typeToScope(type); + final int scopeOrdinal = scope.ordinal(); + + final ORID identity = id.getIdentity().copy(); + if (!pushInHook(identity)) + return ORecordHook.RESULT.RECORD_NOT_CHANGED; + + try { + final ORecord rec = id.getRecord(); + if (rec == null) + return ORecordHook.RESULT.RECORD_NOT_CHANGED; + + final OScenarioThreadLocal.RUN_MODE runMode = OScenarioThreadLocal.INSTANCE.getRunMode(); + + boolean recordChanged = false; + for (ORecordHook hook : hooksByScope[scopeOrdinal]) { + switch (runMode) { + case DEFAULT: // NON_DISTRIBUTED OR PROXIED DB + if (getStorage().isDistributed() + && hook.getDistributedExecutionMode() == ORecordHook.DISTRIBUTED_EXECUTION_MODE.TARGET_NODE) + // SKIP + continue; + break; // TARGET NODE + case RUNNING_DISTRIBUTED: + if (hook.getDistributedExecutionMode() == ORecordHook.DISTRIBUTED_EXECUTION_MODE.SOURCE_NODE) + continue; + } + + final ORecordHook.RESULT res = hook.onTrigger(type, rec); + + if (res == ORecordHook.RESULT.RECORD_CHANGED) + recordChanged = true; + else if (res == ORecordHook.RESULT.SKIP_IO) + // SKIP IO OPERATION + return res; + else if (res == ORecordHook.RESULT.SKIP) + // SKIP NEXT HOOKS AND RETURN IT + return res; + else if (res == ORecordHook.RESULT.RECORD_REPLACED) + return res; + } + + return recordChanged ? ORecordHook.RESULT.RECORD_CHANGED : ORecordHook.RESULT.RECORD_NOT_CHANGED; + + } finally { + popInHook(identity); + } + } + + /** + * {@inheritDoc} + */ + public boolean isValidationEnabled() { + return (Boolean) get(ATTRIBUTES.VALIDATION); + } + + /** + * {@inheritDoc} + */ + public DB setValidationEnabled(final boolean iEnabled) { + set(ATTRIBUTES.VALIDATION, iEnabled); + return (DB) this; + } + + public ORecordConflictStrategy getConflictStrategy() { + checkIfActive(); + return getStorage().getConflictStrategy(); + } + + public ODatabaseDocumentTxOrig setConflictStrategy(final String iStrategyName) { + checkIfActive(); + getStorage().setConflictStrategy(Orient.instance().getRecordConflictStrategy().getStrategy(iStrategyName)); + return this; + } + + public ODatabaseDocumentTxOrig setConflictStrategy(final ORecordConflictStrategy iResolver) { + checkIfActive(); + getStorage().setConflictStrategy(iResolver); + return this; + } + + @Override + public OContextConfiguration getConfiguration() { + checkIfActive(); + if (storage != null) + return storage.getConfiguration().getContextConfiguration(); + return null; + } + + @Override + public boolean declareIntent(final OIntent iIntent) { + checkIfActive(); + + if (currentIntent != null) { + if (iIntent != null && iIntent.getClass().equals(currentIntent.getClass())) + // SAME INTENT: JUMP IT + return false; + + // END CURRENT INTENT + currentIntent.end(this); + } + + currentIntent = iIntent; + + if (iIntent != null) + iIntent.begin(this); + + return true; + } + + @Override + public boolean exists() { + if (status == STATUS.OPEN) + return true; + + if (storage == null) + storage = Orient.instance().loadStorage(url); + + return storage.exists(); + } + + @Override + public void close() { + checkIfActive(); + + try { + localCache.shutdown(); + + if (isClosed()) { + status = STATUS.CLOSED; + return; + } + + try { + commit(true); + } catch (Exception e) { + OLogManager.instance().error(this, "Exception during commit of active transaction", e); + } + + if (status != STATUS.OPEN) + return; + + callOnCloseListeners(); + + if (currentIntent != null) { + currentIntent.end(this); + currentIntent = null; + } + sharedContext = null; + status = STATUS.CLOSED; + + localCache.clear(); + + if (!keepStorageOpen && storage != null) + storage.close(); + + } finally { + // ALWAYS RESET TL + ODatabaseRecordThreadLocal.INSTANCE.remove(); + clearOwner(); + } + } + + protected void clearOwner() { + if (!ownerProtection) + return; + owner.set(null); + } + + @Override + public STATUS getStatus() { + return status; + } + + @Override + public long getSize() { + checkIfActive(); + return storage.getSize(); + } + + @Override + public String getName() { + return storage != null ? storage.getName() : url; + } + + @Override + public String getURL() { + return url != null ? url : storage.getURL(); + } + + @Override + public int getDefaultClusterId() { + checkIfActive(); + return storage.getDefaultClusterId(); + } + + @Override + public int getClusters() { + checkIfActive(); + return storage.getClusters(); + } + + @Override + public boolean existsCluster(final String iClusterName) { + checkIfActive(); + return storage.getClusterNames().contains(iClusterName.toLowerCase()); + } + + @Override + public Collection getClusterNames() { + checkIfActive(); + return storage.getClusterNames(); + } + + @Override + public int getClusterIdByName(final String iClusterName) { + if (iClusterName == null) + return -1; + + checkIfActive(); + return storage.getClusterIdByName(iClusterName.toLowerCase()); + } + + @Override + public String getClusterNameById(final int iClusterId) { + if (iClusterId < 0) + return null; + + checkIfActive(); + return storage.getPhysicalClusterNameById(iClusterId); + } + + @Override + public long getClusterRecordSizeByName(final String clusterName) { + checkIfActive(); + try { + return storage.getClusterById(getClusterIdByName(clusterName)).getRecordsSize(); + } catch (Exception e) { + throw OException.wrapException(new ODatabaseException("Error on reading records size for cluster '" + clusterName + "'"), e); + } + } + + @Override + public long getClusterRecordSizeById(final int clusterId) { + checkIfActive(); + try { + return storage.getClusterById(clusterId).getRecordsSize(); + } catch (Exception e) { + throw OException + .wrapException(new ODatabaseException("Error on reading records size for cluster with id '" + clusterId + "'"), e); + } + } + + @Override + public boolean isClosed() { + return status == STATUS.CLOSED || storage.isClosed(); + } + + @Override + public int addCluster(final String iClusterName, final Object... iParameters) { + checkIfActive(); + return storage.addCluster(iClusterName, false, iParameters); + } + + @Override + public int addCluster(final String iClusterName, final int iRequestedId, final Object... iParameters) { + checkIfActive(); + return storage.addCluster(iClusterName, iRequestedId, false, iParameters); + } + + @Override + public boolean dropCluster(final String iClusterName, final boolean iTruncate) { + checkIfActive(); + final int clusterId = getClusterIdByName(iClusterName); + OSchemaProxy schema = metadata.getSchema(); + OClass clazz = schema.getClassByClusterId(clusterId); + if (clazz != null) + clazz.removeClusterId(clusterId); + if (schema.getBlobClusters().contains(clusterId)) + schema.removeBlobCluster(iClusterName); + getLocalCache().freeCluster(clusterId); + return storage.dropCluster(iClusterName, iTruncate); + } + + @Override + public boolean dropCluster(final int iClusterId, final boolean iTruncate) { + checkIfActive(); + + checkSecurity(ORule.ResourceGeneric.CLUSTER, ORole.PERMISSION_DELETE, getClusterNameById(iClusterId)); + + OSchemaProxy schema = metadata.getSchema(); + final OClass clazz = schema.getClassByClusterId(iClusterId); + if (clazz != null) + clazz.removeClusterId(iClusterId); + getLocalCache().freeCluster(iClusterId); + if (schema.getBlobClusters().contains(iClusterId)) + schema.removeBlobCluster(getClusterNameById(iClusterId)); + + return storage.dropCluster(iClusterId, iTruncate); + } + + @Override + public Object setProperty(final String iName, final Object iValue) { + if (iValue == null) + return properties.remove(iName.toLowerCase()); + else + return properties.put(iName.toLowerCase(), iValue); + } + + @Override + public Object getProperty(final String iName) { + return properties.get(iName.toLowerCase()); + } + + @Override + public Iterator> getProperties() { + return properties.entrySet().iterator(); + } + + @Override + public Object get(final ATTRIBUTES iAttribute) { + checkIfActive(); + + if (iAttribute == null) + throw new IllegalArgumentException("attribute is null"); + + switch (iAttribute) { + case STATUS: + return getStatus(); + case DEFAULTCLUSTERID: + return getDefaultClusterId(); + case TYPE: + return getMetadata().getImmutableSchemaSnapshot().existsClass("V") ? "graph" : "document"; + case DATEFORMAT: + return storage.getConfiguration().dateFormat; + + case DATETIMEFORMAT: + return storage.getConfiguration().dateTimeFormat; + + case TIMEZONE: + return storage.getConfiguration().getTimeZone().getID(); + + case LOCALECOUNTRY: + return storage.getConfiguration().getLocaleCountry(); + + case LOCALELANGUAGE: + return storage.getConfiguration().getLocaleLanguage(); + + case CHARSET: + return storage.getConfiguration().getCharset(); + + case CUSTOM: + return storage.getConfiguration().getProperties(); + + case CLUSTERSELECTION: + return storage.getConfiguration().getClusterSelection(); + + case MINIMUMCLUSTERS: + return storage.getConfiguration().getMinimumClusters(); + + case CONFLICTSTRATEGY: + return storage.getConfiguration().getConflictStrategy(); + + case VALIDATION: + return storage.getConfiguration().isValidationEnabled(); + } + + return null; + } + + @Override + public DB set(final ATTRIBUTES iAttribute, final Object iValue) { + checkIfActive(); + + if (iAttribute == null) + throw new IllegalArgumentException("attribute is null"); + + final String stringValue = OIOUtils.getStringContent(iValue != null ? iValue.toString() : null); + + switch (iAttribute) { + case STATUS: + if (stringValue == null) + throw new IllegalArgumentException("DB status can't be null"); + setStatus(STATUS.valueOf(stringValue.toUpperCase(Locale.ENGLISH))); + break; + + case DEFAULTCLUSTERID: + if (iValue != null) { + if (iValue instanceof Number) + storage.setDefaultClusterId(((Number) iValue).intValue()); + else + storage.setDefaultClusterId(storage.getClusterIdByName(iValue.toString())); + } + break; + + case TYPE: + throw new IllegalArgumentException("Database type cannot be changed at run-time"); + + case DATEFORMAT: + if (stringValue == null) + throw new IllegalArgumentException("date format is null"); + + // CHECK FORMAT + new SimpleDateFormat(stringValue).format(new Date()); + + storage.getConfiguration().dateFormat = stringValue; + storage.getConfiguration().update(); + break; + + case DATETIMEFORMAT: + if (stringValue == null) + throw new IllegalArgumentException("date format is null"); + + // CHECK FORMAT + new SimpleDateFormat(stringValue).format(new Date()); + + storage.getConfiguration().dateTimeFormat = stringValue; + storage.getConfiguration().update(); + break; + + case TIMEZONE: + if (stringValue == null) + throw new IllegalArgumentException("Timezone can't be null"); + + // for backward compatibility, until 2.1.13 OrientDB accepted timezones in lowercase as well + TimeZone timeZoneValue = TimeZone.getTimeZone(stringValue.toUpperCase()); + if (timeZoneValue.equals(TimeZone.getTimeZone("GMT"))) { + timeZoneValue = TimeZone.getTimeZone(stringValue); + } + + storage.getConfiguration().setTimeZone(timeZoneValue); + storage.getConfiguration().update(); + break; + + case LOCALECOUNTRY: + storage.getConfiguration().setLocaleCountry(stringValue); + storage.getConfiguration().update(); + break; + + case LOCALELANGUAGE: + storage.getConfiguration().setLocaleLanguage(stringValue); + storage.getConfiguration().update(); + break; + + case CHARSET: + storage.getConfiguration().setCharset(stringValue); + storage.getConfiguration().update(); + break; + + case CUSTOM: + int indx = stringValue != null ? stringValue.indexOf('=') : -1; + if (indx < 0) { + if ("clear".equalsIgnoreCase(stringValue)) { + clearCustomInternal(); + } else + throw new IllegalArgumentException("Syntax error: expected = or clear, instead found: " + iValue); + } else { + String customName = stringValue.substring(0, indx).trim(); + String customValue = stringValue.substring(indx + 1).trim(); + if (customValue.isEmpty()) + removeCustomInternal(customName); + else + setCustomInternal(customName, customValue); + } + break; + + case CLUSTERSELECTION: + storage.getConfiguration().setClusterSelection(stringValue); + storage.getConfiguration().update(); + break; + + case MINIMUMCLUSTERS: + if (iValue != null) { + if (iValue instanceof Number) + storage.getConfiguration().setMinimumClusters(((Number) iValue).intValue()); + else + storage.getConfiguration().setMinimumClusters(Integer.parseInt(stringValue)); + } else + // DEFAULT = 1 + storage.getConfiguration().setMinimumClusters(1); + + storage.getConfiguration().update(); + break; + + case CONFLICTSTRATEGY: + storage.setConflictStrategy(Orient.instance().getRecordConflictStrategy().getStrategy(stringValue)); + storage.getConfiguration().setConflictStrategy(stringValue); + storage.getConfiguration().update(); + break; + + case VALIDATION: + storage.getConfiguration().setValidation(Boolean.parseBoolean(stringValue)); + storage.getConfiguration().update(); + break; + + default: + throw new IllegalArgumentException("Option '" + iAttribute + "' not supported on alter database"); + + } + + return (DB) this; + } + + public DB setCustom(final String name, final Object iValue) { + checkIfActive(); + + if ("clear".equalsIgnoreCase(name) && iValue == null) { + clearCustomInternal(); + } else { + String customName = name; + String customValue = iValue == null ? null : "" + iValue; + if (customName == null || customValue.isEmpty()) + removeCustomInternal(customName); + else + setCustomInternal(customName, customValue); + } + + return (DB) this; + } + + @Override + public ORecordMetadata getRecordMetadata(final ORID rid) { + checkIfActive(); + return storage.getRecordMetadata(rid); + } + + public OTransaction getTransaction() { + checkIfActive(); + return currentTx; + } + + @SuppressWarnings("unchecked") + @Override + public RET load(final ORecord iRecord, final String iFetchPlan) { + checkIfActive(); + return (RET) currentTx.loadRecord(iRecord.getIdentity(), iRecord, iFetchPlan, false, false, OStorage.LOCKING_STRATEGY.DEFAULT); + } + + @SuppressWarnings("unchecked") + @Override + @Deprecated + public RET load(ORecord iRecord, String iFetchPlan, boolean iIgnoreCache, boolean loadTombstone, + OStorage.LOCKING_STRATEGY iLockingStrategy) { + checkIfActive(); + return (RET) currentTx + .loadRecord(iRecord.getIdentity(), iRecord, iFetchPlan, iIgnoreCache, !iIgnoreCache, loadTombstone, iLockingStrategy); + } + + @SuppressWarnings("unchecked") + @Override + @Deprecated + public RET load(final ORecord iRecord, final String iFetchPlan, final boolean iIgnoreCache, + final boolean iUpdateCache, final boolean loadTombstone, final OStorage.LOCKING_STRATEGY iLockingStrategy) { + checkIfActive(); + return (RET) currentTx + .loadRecord(iRecord.getIdentity(), iRecord, iFetchPlan, iIgnoreCache, iUpdateCache, loadTombstone, iLockingStrategy); + } + + @SuppressWarnings("unchecked") + @Override + public RET load(final ORecord iRecord) { + checkIfActive(); + return (RET) currentTx.loadRecord(iRecord.getIdentity(), iRecord, null, false); + } + + @SuppressWarnings("unchecked") + @Override + public RET load(final ORID recordId) { + return (RET) currentTx.loadRecord(recordId, null, null, false); + } + + @SuppressWarnings("unchecked") + @Override + public RET load(final ORID iRecordId, final String iFetchPlan) { + checkIfActive(); + return (RET) currentTx.loadRecord(iRecordId, null, iFetchPlan, false); + } + + @SuppressWarnings("unchecked") + public RET loadIfVersionIsNotLatest(final ORID rid, final int recordVersion, String fetchPlan, + boolean ignoreCache) throws ORecordNotFoundException { + checkIfActive(); + return (RET) currentTx.loadRecordIfVersionIsNotLatest(rid, recordVersion, fetchPlan, ignoreCache); + } + + @SuppressWarnings("unchecked") + @Override + @Deprecated + public RET load(final ORID iRecordId, String iFetchPlan, final boolean iIgnoreCache, + final boolean loadTombstone, OStorage.LOCKING_STRATEGY iLockingStrategy) { + checkIfActive(); + return (RET) currentTx.loadRecord(iRecordId, null, iFetchPlan, iIgnoreCache, loadTombstone, iLockingStrategy); + } + + @SuppressWarnings("unchecked") + @Override + @Deprecated + public RET load(final ORID iRecordId, String iFetchPlan, final boolean iIgnoreCache, + final boolean iUpdateCache, final boolean loadTombstone, OStorage.LOCKING_STRATEGY iLockingStrategy) { + checkIfActive(); + return (RET) currentTx.loadRecord(iRecordId, null, iFetchPlan, iIgnoreCache, iUpdateCache, loadTombstone, iLockingStrategy); + } + + @SuppressWarnings("unchecked") + public RET reload(final ORecord iRecord) { + return reload(iRecord, null, false); + } + + @SuppressWarnings("unchecked") + public RET reload(final ORecord iRecord, final String iFetchPlan) { + return reload(iRecord, iFetchPlan, false); + } + + @SuppressWarnings("unchecked") + @Override + public RET reload(final ORecord iRecord, final String iFetchPlan, final boolean iIgnoreCache) { + return reload(iRecord, iFetchPlan, iIgnoreCache, true); + } + + @Override + public RET reload(ORecord record, String fetchPlan, boolean ignoreCache, boolean force) { + checkIfActive(); + + final ORecord loadedRecord = currentTx.reloadRecord(record.getIdentity(), record, fetchPlan, ignoreCache, force); + + if (loadedRecord != null && record != loadedRecord) { + record.fromStream(loadedRecord.toStream()); + ORecordInternal.setVersion(record, loadedRecord.getVersion()); + } else if (loadedRecord == null) { + throw new ORecordNotFoundException(record.getIdentity()); + } + + return (RET) record; + } + + /** + * Deletes the record without checking the version. + */ + public ODatabaseDocument delete(final ORID iRecord) { + checkOpeness(); + checkIfActive(); + + final ORecord rec = iRecord.getRecord(); + if (rec != null) + rec.delete(); + return this; + } + + @Override + public boolean hide(ORID rid) { + checkOpeness(); + checkIfActive(); + + if (currentTx.isActive()) + throw new ODatabaseException("This operation can be executed only in non transaction mode"); + + return executeHideRecord(rid, OPERATION_MODE.SYNCHRONOUS); + } + + @Override + public OBinarySerializerFactory getSerializerFactory() { + return componentsFactory.binarySerializerFactory; + } + + public ODatabaseDocument begin(final OTransaction iTx) { + checkOpeness(); + checkIfActive(); + + if (currentTx.isActive() && iTx.equals(currentTx)) { + currentTx.begin(); + return this; + } + + currentTx.rollback(true, 0); + + // WAKE UP LISTENERS + for (ODatabaseListener listener : browseListeners()) + try { + listener.onBeforeTxBegin(this); + } catch (Exception e) { + final String message = "Error before the transaction begin"; + + OLogManager.instance().error(this, message, e); + throw OException.wrapException(new OTransactionBlockedException(message), e); + } + + currentTx = iTx; + currentTx.begin(); + + return this; + } + + /** + * {@inheritDoc} + */ + public RET load(final ORecord iRecord, final String iFetchPlan, final boolean iIgnoreCache) { + return (RET) executeReadRecord((ORecordId) iRecord.getIdentity(), iRecord, -1, iFetchPlan, iIgnoreCache, !iIgnoreCache, false, + OStorage.LOCKING_STRATEGY.NONE, new SimpleRecordReader(prefetchRecords)); + } + + /** + * This method is internal, it can be subject to signature change or be removed, do not use. + * + * @Internal + */ + public Set executeReadRecords(final Set iRids, final boolean ignoreCache) { + checkOpeness(); + checkIfActive(); + + getMetadata().makeThreadLocalSchemaSnapshot(); + ORecordSerializationContext.pushContext(); + try { + + final Set records = new HashSet(iRids.size() > 0 ? iRids.size() : 1); + + if (iRids.isEmpty()) + return records; + + final Collection rids = new ArrayList(iRids); + + for (Iterator it = rids.iterator(); it.hasNext(); ) { + final ORecordId rid = it.next(); + + // SEARCH IN LOCAL TX + ORecord record = getTransaction().getRecord(rid); + if (record == OTransactionRealAbstract.DELETED_RECORD) { + // DELETED IN TX + it.remove(); + continue; + } + + if (record == null && !ignoreCache) + // SEARCH INTO THE CACHE + record = getLocalCache().findRecord(rid); + + if (record != null) { + // FOUND FROM CACHE + records.add(record); + it.remove(); + } + } + + final Collection> rawRecords = ((OAbstractPaginatedStorage) storage.getUnderlying()) + .readRecords(rids); + for (OPair entry : rawRecords) { + // NO SAME RECORD TYPE: CAN'T REUSE OLD ONE BUT CREATE A NEW ONE FOR IT + final ORecord record = Orient.instance().getRecordFactoryManager().newInstance(entry.value.recordType); + ORecordInternal.fill(record, entry.key, entry.value.version, entry.value.buffer, false); + records.add(record); + } + + return records; + + } finally { + ORecordSerializationContext.pullContext(); + getMetadata().clearThreadLocalSchemaSnapshot(); + } + } + + @Override + public void setPrefetchRecords(boolean prefetchRecords) { + this.prefetchRecords = prefetchRecords; + } + + @Override + public boolean isPrefetchRecords() { + return prefetchRecords; + } + + /** + * This method is internal, it can be subject to signature change or be removed, do not use. + * + * @Internal + */ + public RET executeReadRecord(final ORecordId rid, ORecord iRecord, final int recordVersion, + final String fetchPlan, final boolean ignoreCache, final boolean iUpdateCache, final boolean loadTombstones, + final OStorage.LOCKING_STRATEGY lockingStrategy, RecordReader recordReader) { + checkOpeness(); + checkIfActive(); + + getMetadata().makeThreadLocalSchemaSnapshot(); + ORecordSerializationContext.pushContext(); + try { + checkSecurity(ORule.ResourceGeneric.CLUSTER, ORole.PERMISSION_READ, getClusterNameById(rid.getClusterId())); + + // SEARCH IN LOCAL TX + ORecord record = getTransaction().getRecord(rid); + if (record == OTransactionRealAbstract.DELETED_RECORD) + // DELETED IN TX + return null; + + if (record == null && !ignoreCache) + // SEARCH INTO THE CACHE + record = getLocalCache().findRecord(rid); + + if (record != null) { + if (iRecord != null) { + iRecord.fromStream(record.toStream()); + ORecordInternal.setVersion(iRecord, record.getVersion()); + record = iRecord; + } + + OFetchHelper.checkFetchPlanValid(fetchPlan); + if (callbackHooks(ORecordHook.TYPE.BEFORE_READ, record) == ORecordHook.RESULT.SKIP) + return null; + + if (record.getInternalStatus() == ORecordElement.STATUS.NOT_LOADED) + record.reload(); + + if (lockingStrategy == OStorage.LOCKING_STRATEGY.KEEP_SHARED_LOCK) { + OLogManager.instance() + .warn(this, "You use deprecated record locking strategy: %s it may lead to deadlocks " + lockingStrategy); + record.lock(false); + + } else if (lockingStrategy == OStorage.LOCKING_STRATEGY.KEEP_EXCLUSIVE_LOCK) { + OLogManager.instance() + .warn(this, "You use deprecated record locking strategy: %s it may lead to deadlocks " + lockingStrategy); + record.lock(true); + } + + callbackHooks(ORecordHook.TYPE.AFTER_READ, record); + if (record instanceof ODocument) + ODocumentInternal.checkClass((ODocument) record, this); + return (RET) record; + } + + final ORawBuffer recordBuffer; + if (!rid.isValid()) + recordBuffer = null; + else { + OFetchHelper.checkFetchPlanValid(fetchPlan); + + int version; + if (iRecord != null) + version = iRecord.getVersion(); + else + version = recordVersion; + + recordBuffer = recordReader.readRecord(storage, rid, fetchPlan, ignoreCache, version); + } + + if (recordBuffer == null) + return null; + + if (iRecord == null || ORecordInternal.getRecordType(iRecord) != recordBuffer.recordType) + // NO SAME RECORD TYPE: CAN'T REUSE OLD ONE BUT CREATE A NEW ONE FOR IT + iRecord = Orient.instance().getRecordFactoryManager().newInstance(recordBuffer.recordType); + + ORecordInternal.fill(iRecord, rid, recordBuffer.version, recordBuffer.buffer, false); + + if (iRecord instanceof ODocument) + ODocumentInternal.checkClass((ODocument) iRecord, this); + + if (ORecordVersionHelper.isTombstone(iRecord.getVersion())) + return (RET) iRecord; + + if (callbackHooks(ORecordHook.TYPE.BEFORE_READ, iRecord) == ORecordHook.RESULT.SKIP) + return null; + + iRecord.fromStream(recordBuffer.buffer); + + callbackHooks(ORecordHook.TYPE.AFTER_READ, iRecord); + + if (iUpdateCache) + getLocalCache().updateRecord(iRecord); + + return (RET) iRecord; + } catch (OOfflineClusterException t) { + throw t; + } catch (ORecordNotFoundException t) { + throw t; + } catch (Throwable t) { + if (rid.isTemporary()) + throw OException.wrapException(new ODatabaseException("Error on retrieving record using temporary RID: " + rid), t); + else + throw OException.wrapException(new ODatabaseException( + "Error on retrieving record " + rid + " (cluster: " + storage.getPhysicalClusterNameById(rid.clusterId) + ")"), t); + } finally { + ORecordSerializationContext.pullContext(); + getMetadata().clearThreadLocalSchemaSnapshot(); + } + } + + public int assignAndCheckCluster(ORecord record, String iClusterName) { + ORecordId rid = (ORecordId) record.getIdentity(); + // if provided a cluster name use it. + if (rid.clusterId <= ORID.CLUSTER_POS_INVALID && iClusterName != null) { + rid.clusterId = getClusterIdByName(iClusterName); + if (rid.clusterId == -1) + throw new IllegalArgumentException("Cluster name '" + iClusterName + "' is not configured"); + + } + OClass schemaClass = null; + // if cluster id is not set yet try to find it out + if (rid.getClusterId() <= ORID.CLUSTER_ID_INVALID && storage.isAssigningClusterIds()) { + if (record instanceof ODocument) { + schemaClass = ODocumentInternal.getImmutableSchemaClass(((ODocument) record)); + if (schemaClass != null) { + if (schemaClass.isAbstract()) + throw new OSchemaException("Document belongs to abstract class " + schemaClass.getName() + " and cannot be saved"); + rid.clusterId = schemaClass.getClusterForNewInstance((ODocument) record); + } else + rid.clusterId = getDefaultClusterId(); + } else { + rid.clusterId = getDefaultClusterId(); + if (record instanceof OBlob && rid.clusterId != ORID.CLUSTER_ID_INVALID) { + // Set blobClusters = getMetadata().getSchema().getBlobClusters(); + // if (!blobClusters.contains(rid.clusterId) && rid.clusterId != getDefaultClusterId() && rid.clusterId != 0) { + // if (iClusterName == null) + // iClusterName = getClusterNameById(rid.clusterId); + // throw new IllegalArgumentException( + // "Cluster name '" + iClusterName + "' (id=" + rid.clusterId + ") is not configured to store blobs, valid are " + // + blobClusters.toString()); + // } + } + } + } else if (record instanceof ODocument) + schemaClass = ODocumentInternal.getImmutableSchemaClass(((ODocument) record)); + // If the cluster id was set check is validity + if (rid.getClusterId() > ORID.CLUSTER_ID_INVALID) { + if (schemaClass != null) { + String messageClusterName = getClusterNameById(rid.getClusterId()); + checkRecordClass(schemaClass, messageClusterName, rid); + if (!schemaClass.hasClusterId(rid.getClusterId())) { + throw new IllegalArgumentException( + "Cluster name '" + messageClusterName + "' (id=" + rid.getClusterId() + ") is not configured to store the class '" + + schemaClass.getName() + "', valid are " + Arrays.toString(schemaClass.getClusterIds())); + } + } + } + return rid.getClusterId(); + } + + public RET executeSaveEmptyRecord(ORecord record, String clusterName) { + ORecordId rid = (ORecordId) record.getIdentity(); + assert rid.isNew(); + + ORecordInternal.onBeforeIdentityChanged(record); + int id = assignAndCheckCluster(record, clusterName); + clusterName = getClusterNameById(id); + checkSecurity(ORule.ResourceGeneric.CLUSTER, ORole.PERMISSION_CREATE, clusterName); + + byte[] content = getSerializer().writeClassOnly(record); + + final OStorageOperationResult ppos = storage + .createRecord(rid, content, record.getVersion(), recordType, OPERATION_MODE.SYNCHRONOUS.ordinal(), null); + + ORecordInternal.setVersion(record, ppos.getResult().recordVersion); + ((ORecordId) record.getIdentity()).copyFrom(rid); + ORecordInternal.onAfterIdentityChanged(record); + + return (RET) record; + } + + /** + * This method is internal, it can be subject to signature change or be removed, do not use. + * + * @Internal + */ + public RET executeSaveRecord(final ORecord record, String clusterName, final int ver, + final OPERATION_MODE mode, boolean forceCreate, final ORecordCallback recordCreatedCallback, + ORecordCallback recordUpdatedCallback) { + checkOpeness(); + checkIfActive(); + if (!record.isDirty()) + return (RET) record; + + final ORecordId rid = (ORecordId) record.getIdentity(); + + if (rid == null) + throw new ODatabaseException( + "Cannot create record because it has no identity. Probably is not a regular record or contains projections of fields rather than a full record"); + + record.setInternalStatus(ORecordElement.STATUS.MARSHALLING); + try { + + byte[] stream = null; + final OStorageOperationResult operationResult; + + getMetadata().makeThreadLocalSchemaSnapshot(); + if (record instanceof ODocument) + ODocumentInternal.checkClass((ODocument) record, this); + ORecordSerializationContext.pushContext(); + final boolean isNew = forceCreate || rid.isNew(); + try { + + final ORecordHook.TYPE triggerType; + if (isNew) { + // NOTIFY IDENTITY HAS CHANGED + ORecordInternal.onBeforeIdentityChanged(record); + int id = assignAndCheckCluster(record, clusterName); + clusterName = getClusterNameById(id); + checkSecurity(ORule.ResourceGeneric.CLUSTER, ORole.PERMISSION_CREATE, clusterName); + triggerType = ORecordHook.TYPE.BEFORE_CREATE; + } else { + clusterName = getClusterNameById(record.getIdentity().getClusterId()); + checkSecurity(ORule.ResourceGeneric.CLUSTER, ORole.PERMISSION_UPDATE, clusterName); + triggerType = ORecordHook.TYPE.BEFORE_UPDATE; + } + stream = getSerializer().toStream(record, false); + + final ORecordHook.RESULT hookResult = callbackHooks(triggerType, record); + + if (hookResult == ORecordHook.RESULT.RECORD_CHANGED) { + if (record instanceof ODocument) + ((ODocument) record).validate(); + stream = updateStream(record); + } else if (hookResult == ORecordHook.RESULT.SKIP_IO) + return (RET) record; + else if (hookResult == ORecordHook.RESULT.RECORD_REPLACED) + // RETURNED THE REPLACED RECORD + return (RET) OHookReplacedRecordThreadLocal.INSTANCE.get(); + + ORecordSaveThreadLocal.setLast(record); + try { + // SAVE IT + boolean updateContent = ORecordInternal.isContentChanged(record); + byte[] content = (stream == null) ? OCommonConst.EMPTY_BYTE_ARRAY : stream; + byte recordType = ORecordInternal.getRecordType(record); + final int modeIndex = mode.ordinal(); + + // CHECK IF RECORD TYPE IS SUPPORTED + Orient.instance().getRecordFactoryManager().getRecordTypeClass(recordType); + + if (forceCreate || ORecordId.isNew(rid.clusterPosition)) { + // CREATE + final OStorageOperationResult ppos = storage + .createRecord(rid, content, ver, recordType, modeIndex, (ORecordCallback) recordCreatedCallback); + operationResult = new OStorageOperationResult(ppos.getResult().recordVersion, ppos.isMoved()); + + } else { + // UPDATE + operationResult = storage.updateRecord(rid, updateContent, content, ver, recordType, modeIndex, recordUpdatedCallback); + } + + final int version = operationResult.getResult(); + + if (isNew) { + // UPDATE INFORMATION: CLUSTER ID+POSITION + ((ORecordId) record.getIdentity()).copyFrom(rid); + // NOTIFY IDENTITY HAS CHANGED + ORecordInternal.onAfterIdentityChanged(record); + // UPDATE INFORMATION: CLUSTER ID+POSITION + } + + if (operationResult.getModifiedRecordContent() != null) + stream = operationResult.getModifiedRecordContent(); + else if (version > record.getVersion() + 1) + // IN CASE OF REMOTE CONFLICT STRATEGY FORCE UNLOAD DUE TO INVALID CONTENT + record.unload(); + + ORecordInternal.fill(record, rid, version, stream, false); + + callbackHookSuccess(record, isNew, stream, operationResult); + } catch (Exception t) { + callbackHookFailure(record, isNew, stream); + throw t; + } + } finally { + callbackHookFinalize(record, isNew, stream); + ORecordSerializationContext.pullContext(); + getMetadata().clearThreadLocalSchemaSnapshot(); + ORecordSaveThreadLocal.removeLast(); + } + + if (stream != null && stream.length > 0 && !operationResult.isMoved()) + // ADD/UPDATE IT IN CACHE IF IT'S ACTIVE + getLocalCache().updateRecord(record); + } catch (OException e) { + throw e; + } catch (Exception t) { + if (!ORecordId.isValid(record.getIdentity().getClusterPosition())) + throw OException + .wrapException(new ODatabaseException("Error on saving record in cluster #" + record.getIdentity().getClusterId()), t); + else + throw OException.wrapException(new ODatabaseException("Error on saving record " + record.getIdentity()), t); + + } finally { + record.setInternalStatus(ORecordElement.STATUS.LOADED); + } + return (RET) record; + } + + /** + * This method is internal, it can be subject to signature change or be removed, do not use. + * + * @Internal + */ + public void executeDeleteRecord(OIdentifiable record, final int iVersion, final boolean iRequired, final OPERATION_MODE iMode, + boolean prohibitTombstones) { + checkOpeness(); + checkIfActive(); + + final ORecordId rid = (ORecordId) record.getIdentity(); + + if (rid == null) + throw new ODatabaseException( + "Cannot delete record because it has no identity. Probably was created from scratch or contains projections of fields rather than a full record"); + + if (!rid.isValid()) + return; + + record = record.getRecord(); + if (record == null) + return; + + checkSecurity(ORule.ResourceGeneric.CLUSTER, ORole.PERMISSION_DELETE, getClusterNameById(rid.clusterId)); + + ORecordSerializationContext.pushContext(); + getMetadata().makeThreadLocalSchemaSnapshot(); + try { + if (record instanceof ODocument) { + ODocumentInternal.checkClass((ODocument) record, this); + } + try { + // if cache is switched off record will be unreachable after delete. + ORecord rec = record.getRecord(); + if (rec != null) { + callbackHooks(ORecordHook.TYPE.BEFORE_DELETE, rec); + + if (rec instanceof ODocument) + ORidBagDeleter.deleteAllRidBags((ODocument) rec); + } + + final OStorageOperationResult operationResult; + try { + if (prohibitTombstones) { + final boolean result = storage.cleanOutRecord(rid, iVersion, iMode.ordinal(), null); + if (!result && iRequired) + throw new ORecordNotFoundException(rid); + operationResult = new OStorageOperationResult(result); + } else { + final OStorageOperationResult result = storage.deleteRecord(rid, iVersion, iMode.ordinal(), null); + if (!result.getResult() && iRequired) + throw new ORecordNotFoundException(rid); + operationResult = new OStorageOperationResult(result.getResult()); + } + + if (!operationResult.isMoved() && rec != null) + callbackHooks(ORecordHook.TYPE.AFTER_DELETE, rec); + else if (rec != null) + callbackHooks(ORecordHook.TYPE.DELETE_REPLICATED, rec); + } catch (Exception t) { + callbackHooks(ORecordHook.TYPE.DELETE_FAILED, rec); + throw t; + } finally { + callbackHooks(ORecordHook.TYPE.FINALIZE_DELETION, rec); + } + + clearDocumentTracking(rec); + + // REMOVE THE RECORD FROM 1 AND 2 LEVEL CACHES + if (!operationResult.isMoved()) { + getLocalCache().deleteRecord(rid); + } + + } catch (OException e) { + // RE-THROW THE EXCEPTION + throw e; + + } catch (Exception t) { + // WRAP IT AS ODATABASE EXCEPTION + throw OException + .wrapException(new ODatabaseException("Error on deleting record in cluster #" + record.getIdentity().getClusterId()), + t); + } + } finally { + ORecordSerializationContext.pullContext(); + getMetadata().clearThreadLocalSchemaSnapshot(); + } + } + + /** + * This method is internal, it can be subject to signature change or be removed, do not use. + * + * @Internal + */ + public boolean executeHideRecord(OIdentifiable record, final OPERATION_MODE iMode) { + checkOpeness(); + checkIfActive(); + + final ORecordId rid = (ORecordId) record.getIdentity(); + + if (rid == null) + throw new ODatabaseException( + "Cannot hide record because it has no identity. Probably was created from scratch or contains projections of fields rather than a full record"); + + if (!rid.isValid()) + return false; + + checkSecurity(ORule.ResourceGeneric.CLUSTER, ORole.PERMISSION_DELETE, getClusterNameById(rid.clusterId)); + + getMetadata().makeThreadLocalSchemaSnapshot(); + if (record instanceof ODocument) + ODocumentInternal.checkClass((ODocument) record, this); + ORecordSerializationContext.pushContext(); + try { + + final OStorageOperationResult operationResult; + operationResult = storage.hideRecord(rid, iMode.ordinal(), null); + + // REMOVE THE RECORD FROM 1 AND 2 LEVEL CACHES + if (!operationResult.isMoved()) + getLocalCache().deleteRecord(rid); + + return operationResult.getResult(); + } finally { + ORecordSerializationContext.pullContext(); + getMetadata().clearThreadLocalSchemaSnapshot(); + } + } + + public ODatabaseDocumentTxOrig begin() { + return begin(OTransaction.TXTYPE.OPTIMISTIC); + } + + public ODatabaseDocumentTxOrig begin(final OTransaction.TXTYPE iType) { + checkOpeness(); + checkIfActive(); + + if (currentTx.isActive()) { + if (iType == OTransaction.TXTYPE.OPTIMISTIC && currentTx instanceof OTransactionOptimistic) { + currentTx.begin(); + return this; + } + + currentTx.rollback(true, 0); + } + + // CHECK IT'S NOT INSIDE A HOOK + if (!inHook.isEmpty()) + throw new IllegalStateException("Cannot begin a transaction while a hook is executing"); + + // WAKE UP LISTENERS + for (ODatabaseListener listener : browseListeners()) + try { + listener.onBeforeTxBegin(this); + } catch (Throwable t) { + OLogManager.instance().error(this, "Error before tx begin", t); + } + + switch (iType) { + case NOTX: + setDefaultTransactionMode(); + break; + + case OPTIMISTIC: + currentTx = new OTransactionOptimistic(this); + break; + + case PESSIMISTIC: + throw new UnsupportedOperationException("Pessimistic transaction"); + } + + currentTx.begin(); + return this; + } + + public void setDefaultTransactionMode() { + if (!(currentTx instanceof OTransactionNoTx)) + currentTx = new OTransactionNoTx(this); + } + + /** + * {@inheritDoc} + */ + @Override + public void freeze(final boolean throwException) { + checkOpeness(); + if (!(getStorage() instanceof OFreezableStorageComponent)) { + OLogManager.instance().error(this, + "Only local paginated storage supports freeze. If you are using remote client please use OServerAdmin instead"); + + return; + } + + final long startTime = Orient.instance().getProfiler().startChrono(); + + final OFreezableStorageComponent storage = getFreezableStorage(); + if (storage != null) { + storage.freeze(throwException); + } + + Orient.instance().getProfiler() + .stopChrono("db." + getName() + ".freeze", "Time to freeze the database", startTime, "db.*.freeze"); + } + + /** + * {@inheritDoc} + */ + @Override + public void freeze() { + checkOpeness(); + if (!(getStorage() instanceof OFreezableStorageComponent)) { + OLogManager.instance().error(this, + "Only local paginated storage supports freeze. " + "If you use remote client please use OServerAdmin instead"); + + return; + } + + final long startTime = Orient.instance().getProfiler().startChrono(); + + final OFreezableStorageComponent storage = getFreezableStorage(); + if (storage != null) { + storage.freeze(false); + } + + Orient.instance().getProfiler() + .stopChrono("db." + getName() + ".freeze", "Time to freeze the database", startTime, "db.*.freeze"); + } + + /** + * {@inheritDoc} + */ + @Override + public void release() { + checkOpeness(); + if (!(getStorage() instanceof OFreezableStorageComponent)) { + OLogManager.instance().error(this, + "Only local paginated storage supports release. If you are using remote client please use OServerAdmin instead"); + return; + } + + final long startTime = Orient.instance().getProfiler().startChrono(); + + final OFreezableStorageComponent storage = getFreezableStorage(); + if (storage != null) { + storage.release(); + } + + Orient.instance().getProfiler() + .stopChrono("db." + getName() + ".release", "Time to release the database", startTime, "db.*.release"); + } + + /** + * Creates a new ODocument. + */ + public ODocument newInstance() { + return new ODocument(); + } + + /** + * Creates a document with specific class. + * + * @param iClassName the name of class that should be used as a class of created document. + * @return new instance of document. + */ + @Override + public ODocument newInstance(final String iClassName) { + return new ODocument(iClassName); + } + + /** + * {@inheritDoc} + */ + public ORecordIteratorClass browseClass(final String iClassName) { + return browseClass(iClassName, true); + } + + /** + * {@inheritDoc} + */ + public ORecordIteratorClass browseClass(final String iClassName, final boolean iPolymorphic) { + if (getMetadata().getImmutableSchemaSnapshot().getClass(iClassName) == null) + throw new IllegalArgumentException("Class '" + iClassName + "' not found in current database"); + + checkSecurity(ORule.ResourceGeneric.CLASS, ORole.PERMISSION_READ, iClassName); + return new ORecordIteratorClass(this, this, iClassName, iPolymorphic, false); + } + + /** + * {@inheritDoc} + */ + @Override + public ORecordIteratorCluster browseCluster(final String iClusterName) { + checkSecurity(ORule.ResourceGeneric.CLUSTER, ORole.PERMISSION_READ, iClusterName); + + return new ORecordIteratorCluster(this, this, getClusterIdByName(iClusterName)); + } + + /** + * {@inheritDoc} + */ + @Override + public Iterable getListeners() { + return getListenersCopy(); + } + + /** + * {@inheritDoc} + */ + @Override + @Deprecated + public ORecordIteratorCluster browseCluster(String iClusterName, long startClusterPosition, long endClusterPosition, + boolean loadTombstones) { + checkSecurity(ORule.ResourceGeneric.CLUSTER, ORole.PERMISSION_READ, iClusterName); + + return new ORecordIteratorCluster(this, this, getClusterIdByName(iClusterName), startClusterPosition, + endClusterPosition, loadTombstones, OStorage.LOCKING_STRATEGY.DEFAULT); + } + + /** + * Saves a document to the database. Behavior depends by the current running transaction if any. If no transaction is running then + * changes apply immediately. If an Optimistic transaction is running then the record will be changed at commit time. The current + * transaction will continue to see the record as modified, while others not. If a Pessimistic transaction is running, then an + * exclusive lock is acquired against the record. Current transaction will continue to see the record as modified, while others + * cannot access to it since it's locked. + *

+ * If MVCC is enabled and the version of the document is different by the version stored in the database, then a + * {@link OConcurrentModificationException} exception is thrown.Before to save the document it must be valid following the + * constraints declared in the schema if any (can work also in schema-less mode). To validate the document the + * {@link ODocument#validate()} is called. + * + * @param iRecord Record to save. + * @return The Database instance itself giving a "fluent interface". Useful to call multiple methods in chain. + * @throws OConcurrentModificationException if the version of the document is different by the version contained in the database. + * @throws OValidationException if the document breaks some validation constraints defined in the schema + * @see #setMVCC(boolean), {@link #isMVCC()} + */ + @Override + public RET save(final ORecord iRecord) { + return (RET) save(iRecord, null, OPERATION_MODE.SYNCHRONOUS, false, null, null); + } + + /** + * Saves a document to the database. Behavior depends by the current running transaction if any. If no transaction is running then + * changes apply immediately. If an Optimistic transaction is running then the record will be changed at commit time. The current + * transaction will continue to see the record as modified, while others not. If a Pessimistic transaction is running, then an + * exclusive lock is acquired against the record. Current transaction will continue to see the record as modified, while others + * cannot access to it since it's locked. + *

+ * If MVCC is enabled and the version of the document is different by the version stored in the database, then a + * {@link OConcurrentModificationException} exception is thrown.Before to save the document it must be valid following the + * constraints declared in the schema if any (can work also in schema-less mode). To validate the document the + * {@link ODocument#validate()} is called. + * + * @param iRecord Record to save. + * @param iForceCreate Flag that indicates that record should be created. If record with current rid already exists, exception is thrown + * @param iRecordCreatedCallback callback that is called after creation of new record + * @param iRecordUpdatedCallback callback that is called after record update + * @return The Database instance itself giving a "fluent interface". Useful to call multiple methods in chain. + * @throws OConcurrentModificationException if the version of the document is different by the version contained in the database. + * @throws OValidationException if the document breaks some validation constraints defined in the schema + * @see #setMVCC(boolean), {@link #isMVCC()} + */ + @Override + public RET save(final ORecord iRecord, final OPERATION_MODE iMode, boolean iForceCreate, + final ORecordCallback iRecordCreatedCallback, ORecordCallback iRecordUpdatedCallback) { + return save(iRecord, null, iMode, iForceCreate, iRecordCreatedCallback, iRecordUpdatedCallback); + } + + /** + * Saves a document specifying a cluster where to store the record. Behavior depends by the current running transaction if any. If + * no transaction is running then changes apply immediately. If an Optimistic transaction is running then the record will be + * changed at commit time. The current transaction will continue to see the record as modified, while others not. If a Pessimistic + * transaction is running, then an exclusive lock is acquired against the record. Current transaction will continue to see the + * record as modified, while others cannot access to it since it's locked. + *

+ * If MVCC is enabled and the version of the document is different by the version stored in the database, then a + * {@link OConcurrentModificationException} exception is thrown. Before to save the document it must be valid following the + * constraints declared in the schema if any (can work also in schema-less mode). To validate the document the + * {@link ODocument#validate()} is called. + * + * @param iRecord Record to save + * @param iClusterName Cluster name where to save the record + * @return The Database instance itself giving a "fluent interface". Useful to call multiple methods in chain. + * @throws OConcurrentModificationException if the version of the document is different by the version contained in the database. + * @throws OValidationException if the document breaks some validation constraints defined in the schema + * @see #setMVCC(boolean), {@link #isMVCC()}, ODocument#validate() + */ + @Override + public RET save(final ORecord iRecord, final String iClusterName) { + return (RET) save(iRecord, iClusterName, OPERATION_MODE.SYNCHRONOUS, false, null, null); + } + + /** + * Saves a document specifying a cluster where to store the record. Behavior depends by the current running transaction if any. If + * no transaction is running then changes apply immediately. If an Optimistic transaction is running then the record will be + * changed at commit time. The current transaction will continue to see the record as modified, while others not. If a Pessimistic + * transaction is running, then an exclusive lock is acquired against the record. Current transaction will continue to see the + * record as modified, while others cannot access to it since it's locked. + *

+ * If MVCC is enabled and the version of the document is different by the version stored in the database, then a + * {@link OConcurrentModificationException} exception is thrown. Before to save the document it must be valid following the + * constraints declared in the schema if any (can work also in schema-less mode). To validate the document the + * {@link ODocument#validate()} is called. + * + * @param iRecord Record to save + * @param iClusterName Cluster name where to save the record + * @param iMode Mode of save: synchronous (default) or asynchronous + * @param iForceCreate Flag that indicates that record should be created. If record with current rid already exists, exception is thrown + * @param iRecordCreatedCallback callback that is called after creation of new record + * @param iRecordUpdatedCallback callback that is called after record update + * @return The Database instance itself giving a "fluent interface". Useful to call multiple methods in chain. + * @throws OConcurrentModificationException if the version of the document is different by the version contained in the database. + * @throws OValidationException if the document breaks some validation constraints defined in the schema + * @see #setMVCC(boolean), {@link #isMVCC()}, ODocument#validate() + */ + @Override + public RET save(final ORecord iRecord, String iClusterName, final OPERATION_MODE iMode, + boolean iForceCreate, final ORecordCallback iRecordCreatedCallback, + ORecordCallback iRecordUpdatedCallback) { + checkOpeness(); + + if (!(iRecord instanceof ODocument)) { + assignAndCheckCluster(iRecord, iClusterName); + return (RET) currentTx.saveRecord(iRecord, iClusterName, iMode, iForceCreate, iRecordCreatedCallback, iRecordUpdatedCallback); + } + + ODocument doc = (ODocument) iRecord; + ODocumentInternal.checkClass(doc, this); + if (!getTransaction().isActive() && !storage.isRemote()) + // EXECUTE VALIDATION ONLY IF NOT IN TX + doc.validate(); + ODocumentInternal.convertAllMultiValuesToTrackedVersions(doc); + + if (iForceCreate || !doc.getIdentity().isValid()) { + if (doc.getClassName() != null) + checkSecurity(ORule.ResourceGeneric.CLASS, ORole.PERMISSION_CREATE, doc.getClassName()); + + assignAndCheckCluster(doc, iClusterName); + + } else { + // UPDATE: CHECK ACCESS ON SCHEMA CLASS NAME (IF ANY) + if (doc.getClassName() != null) + checkSecurity(ORule.ResourceGeneric.CLASS, ORole.PERMISSION_UPDATE, doc.getClassName()); + } + + doc = (ODocument) currentTx + .saveRecord(iRecord, iClusterName, iMode, iForceCreate, iRecordCreatedCallback, iRecordUpdatedCallback); + + return (RET) doc; + } + + /** + * Deletes a document. Behavior depends by the current running transaction if any. If no transaction is running then the record is + * deleted immediately. If an Optimistic transaction is running then the record will be deleted at commit time. The current + * transaction will continue to see the record as deleted, while others not. If a Pessimistic transaction is running, then an + * exclusive lock is acquired against the record. Current transaction will continue to see the record as deleted, while others + * cannot access to it since it's locked. + *

+ * If MVCC is enabled and the version of the document is different by the version stored in the database, then a + * {@link OConcurrentModificationException} exception is thrown. + * + * @param record record to delete + * @return The Database instance itself giving a "fluent interface". Useful to call multiple methods in chain. + * @see #setMVCC(boolean), {@link #isMVCC()} + */ + public ODatabaseDocumentTxOrig delete(final ORecord record) { + checkOpeness(); + if (record == null) + throw new ODatabaseException("Cannot delete null document"); + + // CHECK ACCESS ON SCHEMA CLASS NAME (IF ANY) + if (record instanceof ODocument && ((ODocument) record).getClassName() != null) + checkSecurity(ORule.ResourceGeneric.CLASS, ORole.PERMISSION_DELETE, ((ODocument) record).getClassName()); + + try { + currentTx.deleteRecord(record, OPERATION_MODE.SYNCHRONOUS); + } catch (OException e) { + throw e; + } catch (Exception e) { + if (record instanceof ODocument) + throw OException.wrapException(new ODatabaseException( + "Error on deleting record " + record.getIdentity() + " of class '" + ((ODocument) record).getClassName() + "'"), e); + else + throw OException.wrapException(new ODatabaseException("Error on deleting record " + record.getIdentity()), e); + } + return this; + } + + /** + * Returns the number of the records of the class iClassName. + */ + public long countClass(final String iClassName) { + return countClass(iClassName, true); + } + + /** + * Returns the number of the records of the class iClassName considering also sub classes if polymorphic is true. + */ + public long countClass(final String iClassName, final boolean iPolymorphic) { + final OClass cls = getMetadata().getImmutableSchemaSnapshot().getClass(iClassName); + + if (cls == null) + throw new IllegalArgumentException("Class '" + iClassName + "' not found in database"); + + long totalOnDb = cls.count(iPolymorphic); + + long deletedInTx = 0; + long addedInTx = 0; + if (getTransaction().isActive()) + for (ORecordOperation op : getTransaction().getAllRecordEntries()) { + if (op.type == ORecordOperation.DELETED) { + final ORecord rec = op.getRecord(); + if (rec != null && rec instanceof ODocument) { + OClass schemaClass = ((ODocument) rec).getSchemaClass(); + if (iPolymorphic) { + if (schemaClass.isSubClassOf(iClassName)) + deletedInTx++; + } else { + if (iClassName.equals(schemaClass.getName()) || iClassName.equals(schemaClass.getShortName())) + deletedInTx++; + } + } + } + if (op.type == ORecordOperation.CREATED) { + final ORecord rec = op.getRecord(); + if (rec != null && rec instanceof ODocument) { + OClass schemaClass = ((ODocument) rec).getSchemaClass(); + if (iPolymorphic) { + if (schemaClass.isSubClassOf(iClassName)) + addedInTx++; + } else { + if (iClassName.equals(schemaClass.getName()) || iClassName.equals(schemaClass.getShortName())) + addedInTx++; + } + } + } + } + + return (totalOnDb + addedInTx) - deletedInTx; + } + + /** + * {@inheritDoc} + */ + @Override + public ODatabase commit() { + return commit(false); + } + + @Override + public ODatabaseDocument commit(boolean force) throws OTransactionException { + checkOpeness(); + checkIfActive(); + + if (!currentTx.isActive()) + return this; + + if (!force && currentTx.amountOfNestedTxs() > 1) { + currentTx.commit(); + return this; + } + + // WAKE UP LISTENERS + for (ODatabaseListener listener : browseListeners()) + try { + listener.onBeforeTxCommit(this); + } catch (Exception e) { + rollback(force); + + OLogManager.instance().error(this, "Cannot commit the transaction: caught exception on execution of %s.onBeforeTxCommit()", + listener.getClass().getName(), e); + throw OException.wrapException(new OTransactionException( + "Cannot commit the transaction: caught exception on execution of " + listener.getClass().getName() + + "#onBeforeTxCommit()"), e); + } + + try { + currentTx.commit(force); + } catch (RuntimeException e) { + OLogManager.instance().debug(this, "Error on transaction commit", e); + + // WAKE UP ROLLBACK LISTENERS + for (ODatabaseListener listener : browseListeners()) + try { + listener.onBeforeTxRollback(this); + } catch (Throwable t) { + OLogManager.instance().error(this, "Error before transaction rollback", t); + } + + // ROLLBACK TX AT DB LEVEL + currentTx.rollback(false, 0); + getLocalCache().clear(); + + // WAKE UP ROLLBACK LISTENERS + for (ODatabaseListener listener : browseListeners()) + try { + listener.onAfterTxRollback(this); + } catch (Throwable t) { + OLogManager.instance().error(this, "Error after transaction rollback", t); + } + throw e; + } + + // WAKE UP LISTENERS + for (ODatabaseListener listener : browseListeners()) + try { + listener.onAfterTxCommit(this); + } catch (Exception e) { + final String message = + "Error after the transaction has been committed. The transaction remains valid. The exception caught was on execution of " + + listener.getClass() + ".onAfterTxCommit()"; + + OLogManager.instance().error(this, message, e); + + throw OException.wrapException(new OTransactionBlockedException(message), e); + + } + + return this; + } + + @Override + public OUncompletedCommit initiateCommit() { + return initiateCommit(false); + } + + @Override + public OUncompletedCommit initiateCommit(boolean force) { + checkOpeness(); + checkIfActive(); + + if (!currentTx.isActive()) + return OUncompletedCommit.NO_OPERATION; + + if (!force && currentTx.amountOfNestedTxs() > 1) + return new UncompletedCommit(false, currentTx.initiateCommit(false)); + + // WAKE UP LISTENERS + for (ODatabaseListener listener : browseListeners()) + try { + listener.onBeforeTxCommit(this); + } catch (Exception e) { + rollback(force); + + OLogManager.instance().error(this, "Cannot commit the transaction: caught exception on execution of %s.onBeforeTxCommit()", + listener.getClass().getName(), e); + throw OException.wrapException(new OTransactionException( + "Cannot commit the transaction: caught exception on execution of " + listener.getClass().getName() + + "#onBeforeTxCommit()"), e); + } + + try { + return new UncompletedCommit(true, currentTx.initiateCommit(force)); + } catch (RuntimeException e) { + OLogManager.instance().debug(this, "Error on transaction commit", e); + + // WAKE UP ROLLBACK LISTENERS + for (ODatabaseListener listener : browseListeners()) + try { + listener.onBeforeTxRollback(this); + } catch (Throwable t) { + OLogManager.instance().error(this, "Error before transaction rollback", t); + } + + // ROLLBACK TX AT DB LEVEL + currentTx.rollback(false, 0); + getLocalCache().clear(); + + // WAKE UP ROLLBACK LISTENERS + for (ODatabaseListener listener : browseListeners()) + try { + listener.onAfterTxRollback(this); + } catch (Throwable t) { + OLogManager.instance().error(this, "Error after transaction rollback", t); + } + throw e; + } + } + + /** + * {@inheritDoc} + */ + @Override + public ODatabase rollback() { + return rollback(false); + } + + @Override + public ODatabaseDocument rollback(boolean force) throws OTransactionException { + checkOpeness(); + if (currentTx.isActive()) { + + if (!force && currentTx.amountOfNestedTxs() > 1) { + currentTx.rollback(); + return this; + } + + // WAKE UP LISTENERS + for (ODatabaseListener listener : browseListeners()) + try { + listener.onBeforeTxRollback(this); + } catch (Throwable t) { + OLogManager.instance().error(this, "Error before transactional rollback", t); + } + + currentTx.rollback(force, -1); + + // WAKE UP LISTENERS + for (ODatabaseListener listener : browseListeners()) + try { + listener.onAfterTxRollback(this); + } catch (Throwable t) { + OLogManager.instance().error(this, "Error after transaction rollback", t); + } + } + + getLocalCache().clear(); + + return this; + } + + /** + * This method is internal, it can be subject to signature change or be removed, do not use. + * + * @Internal + */ + @Override + public DB getUnderlying() { + throw new UnsupportedOperationException(); + } + + /** + * This method is internal, it can be subject to signature change or be removed, do not use. + * + * @Internal + */ + @Override + public OStorage getStorage() { + return storage; + } + + /** + * This method is internal, it can be subject to signature change or be removed, do not use. + * + * @Internal + */ + @Override + public void replaceStorage(OStorage iNewStorage) { + storage = iNewStorage; + } + + @Override + public V callInLock(final Callable iCallable, final boolean iExclusiveLock) { + return storage.callInLock(iCallable, iExclusiveLock); + } + + @Override + public List backup(final OutputStream out, final Map options, final Callable callable, + final OCommandOutputListener iListener, final int compressionLevel, final int bufferSize) throws IOException { + return storage.backup(out, options, callable, iListener, compressionLevel, bufferSize); + } + + @Override + public void restore(final InputStream in, final Map options, final Callable callable, + final OCommandOutputListener iListener) throws IOException { + if (storage == null) + storage = Orient.instance().loadStorage(url); + + getStorage().restore(in, options, callable, iListener); + + if (!isClosed()) { + loadMetadata(); + sharedContext = null; + } + } + + /** + * {@inheritDoc} + */ + public OSBTreeCollectionManager getSbTreeCollectionManager() { + return getStorage().getSBtreeCollectionManager(); + } + + @Override + public OCurrentStorageComponentsFactory getStorageVersions() { + return componentsFactory; + } + + public ORecordSerializer getSerializer() { + return serializer; + } + + /** + * Sets serializer for the database which will be used for document serialization. + * + * @param serializer the serializer to set. + */ + public void setSerializer(ORecordSerializer serializer) { + this.serializer = serializer; + } + + @Override + public void resetInitialization() { + for (ORecordHook h : hooks.keySet()) + h.onUnregister(); + + hooks.clear(); + compileHooks(); + + close(); + + initialized = false; + } + + @Override + public String incrementalBackup(final String path) { + checkOpeness(); + checkIfActive(); + + return storage.incrementalBackup(path); + } + + @Override + @Deprecated + public DB checkSecurity(final String iResource, final int iOperation) { + final String resourceSpecific = ORule.mapLegacyResourceToSpecificResource(iResource); + final ORule.ResourceGeneric resourceGeneric = ORule.mapLegacyResourceToGenericResource(iResource); + + if (resourceSpecific == null || resourceSpecific.equals("*")) + checkSecurity(resourceGeneric, null, iOperation); + + return checkSecurity(resourceGeneric, resourceSpecific, iOperation); + } + + @Override + @Deprecated + public DB checkSecurity(final String iResourceGeneric, final int iOperation, + final Object iResourceSpecific) { + final ORule.ResourceGeneric resourceGeneric = ORule.mapLegacyResourceToGenericResource(iResourceGeneric); + if (iResourceSpecific == null || iResourceSpecific.equals("*")) + return checkSecurity(resourceGeneric, iOperation, (Object) null); + + return checkSecurity(resourceGeneric, iOperation, iResourceSpecific); + } + + @Override + @Deprecated + public DB checkSecurity(final String iResourceGeneric, final int iOperation, + final Object... iResourcesSpecific) { + final ORule.ResourceGeneric resourceGeneric = ORule.mapLegacyResourceToGenericResource(iResourceGeneric); + return checkSecurity(resourceGeneric, iOperation, iResourcesSpecific); + } + + /** + * @return true if database is obtained from the pool and false otherwise. + */ + @Override + public boolean isPooled() { + return false; + } + + /** + * Use #activateOnCurrentThread instead. + */ + @Deprecated + public void setCurrentDatabaseInThreadLocal() { + activateOnCurrentThread(); + } + + /** + * Activates current database instance on current thread. + */ + @Override + public ODatabaseDocumentTxOrig activateOnCurrentThread() { + final ODatabaseRecordThreadLocal tl = ODatabaseRecordThreadLocal.INSTANCE; + if (tl != null) + tl.set(this); + return this; + } + + @Override + public boolean isActiveOnCurrentThread() { + final ODatabaseRecordThreadLocal tl = ODatabaseRecordThreadLocal.INSTANCE; + final ODatabaseDocumentInternal db = tl != null ? tl.getIfDefined() : null; + return db == this; + } + + protected void checkOpeness() { + if (status == STATUS.CLOSED) + throw new ODatabaseException("Database '" + getURL() + "' is closed"); + } + + private void popInHook(OIdentifiable id) { + inHook.remove(id); + } + + private boolean pushInHook(OIdentifiable id) { + return inHook.add(id); + } + + private void initAtFirstOpen(String iUserName, String iUserPassword) { + if (initialized) + return; + + ORecordSerializerFactory serializerFactory = ORecordSerializerFactory.instance(); + String serializeName = getStorage().getConfiguration().getRecordSerializer(); + if (serializeName == null) + serializeName = ORecordSerializerSchemaAware2CSV.NAME; + serializer = serializerFactory.getFormat(serializeName); + if (serializer == null) + throw new ODatabaseException("RecordSerializer with name '" + serializeName + "' not found "); + if (getStorage().getConfiguration().getRecordSerializerVersion() > serializer.getMinSupportedVersion()) + throw new ODatabaseException("Persistent record serializer version is not support by the current implementation"); + + componentsFactory = getStorage().getComponentsFactory(); + + localCache.startup(); + + user = null; + + loadMetadata(); + + if (!(getStorage() instanceof OStorageProxy)) { + if (metadata.getIndexManager().autoRecreateIndexesAfterCrash()) { + metadata.getIndexManager().recreateIndexes(); + + activateOnCurrentThread(); + user = null; + } + + installHooksEmbedded(); + registerHook(new OCommandCacheHook(this), ORecordHook.HOOK_POSITION.REGULAR); + registerHook(new OSecurityTrackerHook(metadata.getSecurity(), this), ORecordHook.HOOK_POSITION.LAST); + + user = null; + } else if (iUserName != null && iUserPassword != null) { + user = new OImmutableUser(-1, new OUser(iUserName, OUser.encryptPassword(iUserPassword)) + .addRole(new ORole("passthrough", null, ORole.ALLOW_MODES.ALLOW_ALL_BUT))); + installHooksRemote(); + } + + initialized = true; + } + + protected void installHooksEmbedded() { + hooks.clear(); + registerHook(new OClassTrigger(this), ORecordHook.HOOK_POSITION.FIRST); + registerHook(new ORestrictedAccessHook(this), ORecordHook.HOOK_POSITION.FIRST); + registerHook(new OUserTrigger(this), ORecordHook.HOOK_POSITION.EARLY); + registerHook(new OFunctionTrigger(this), ORecordHook.HOOK_POSITION.REGULAR); + registerHook(new OSequenceTrigger(this), ORecordHook.HOOK_POSITION.REGULAR); + registerHook(new OClassIndexManager(this), ORecordHook.HOOK_POSITION.LAST); + registerHook(new OSchedulerTrigger(this), ORecordHook.HOOK_POSITION.LAST); + registerHook(new OLiveQueryHook(this), ORecordHook.HOOK_POSITION.LAST); + } + + protected void installHooksRemote() { + hooks.clear(); + registerHook(new ClassIndexManagerRemote(this), ORecordHook.HOOK_POSITION.LAST); + } + + private void closeOnDelete() { + if (status != STATUS.OPEN) + return; + + if (currentIntent != null) { + currentIntent.end(this); + currentIntent = null; + } + + resetListeners(); + + if (storage != null) + storage.close(true, true); + + storage = null; + status = STATUS.CLOSED; + } + + private void clearCustomInternal() { + storage.getConfiguration().clearProperties(); + } + + private void removeCustomInternal(final String iName) { + setCustomInternal(iName, null); + } + + private void setCustomInternal(final String iName, final String iValue) { + if (iValue == null || "null".equalsIgnoreCase(iValue)) + // REMOVE + storage.getConfiguration().removeProperty(iName); + else + // SET + storage.getConfiguration().setProperty(iName, iValue); + + storage.getConfiguration().update(); + } + + private void callbackHookFailure(ORecord record, boolean wasNew, byte[] stream) { + if (stream != null && stream.length > 0) + callbackHooks(wasNew ? ORecordHook.TYPE.CREATE_FAILED : ORecordHook.TYPE.UPDATE_FAILED, record); + } + + private void callbackHookSuccess(final ORecord record, final boolean wasNew, final byte[] stream, + final OStorageOperationResult operationResult) { + if (stream != null && stream.length > 0) { + final ORecordHook.TYPE hookType; + if (!operationResult.isMoved()) { + hookType = wasNew ? ORecordHook.TYPE.AFTER_CREATE : ORecordHook.TYPE.AFTER_UPDATE; + } else { + hookType = wasNew ? ORecordHook.TYPE.CREATE_REPLICATED : ORecordHook.TYPE.UPDATE_REPLICATED; + } + callbackHooks(hookType, record); + + } + } + + private void callbackHookFinalize(final ORecord record, final boolean wasNew, final byte[] stream) { + if (stream != null && stream.length > 0) { + final ORecordHook.TYPE hookType; + hookType = wasNew ? ORecordHook.TYPE.FINALIZE_CREATION : ORecordHook.TYPE.FINALIZE_UPDATE; + callbackHooks(hookType, record); + + clearDocumentTracking(record); + } + } + + private void clearDocumentTracking(final ORecord record) { + if (record instanceof ODocument && ((ODocument) record).isTrackingChanges()) { + ODocumentInternal.clearTrackData((ODocument) record); + } + } + + private void checkRecordClass(final OClass recordClass, final String iClusterName, final ORecordId rid) { + if (getStorageVersions().classesAreDetectedByClusterId()) { + final OClass clusterIdClass = metadata.getImmutableSchemaSnapshot().getClassByClusterId(rid.clusterId); + if (recordClass == null && clusterIdClass != null || clusterIdClass == null && recordClass != null || (recordClass != null + && !recordClass.equals(clusterIdClass))) + throw new IllegalArgumentException( + "Record saved into cluster '" + iClusterName + "' should be saved with class '" + clusterIdClass + + "' but has been created with class '" + recordClass + "'"); + } + } + + private byte[] updateStream(final ORecord record) { + ORecordSerializationContext.pullContext(); + + ODirtyManager manager = ORecordInternal.getDirtyManager(record); + Set newRecords = manager.getNewRecords(); + Set updatedRecords = manager.getUpdateRecords(); + manager.clearForSave(); + if (newRecords != null) { + for (ORecord newRecord : newRecords) { + if (newRecord != record) + getTransaction().saveRecord(newRecord, null, OPERATION_MODE.SYNCHRONOUS, false, null, null); + } + } + if (updatedRecords != null) { + for (ORecord updatedRecord : updatedRecords) { + if (updatedRecord != record) + getTransaction().saveRecord(updatedRecord, null, OPERATION_MODE.SYNCHRONOUS, false, null, null); + } + } + + ORecordSerializationContext.pushContext(); + ORecordInternal.unsetDirty(record); + record.setDirty(); + return serializer.toStream(record, false); + } + + protected void init() { + currentTx = new OTransactionNoTx(this); + } + + private OFreezableStorageComponent getFreezableStorage() { + OStorage s = getStorage(); + if (s instanceof OFreezableStorageComponent) + return (OFreezableStorageComponent) s; + else { + OLogManager.instance().error(this, "Storage of type " + s.getType() + " does not support freeze operation"); + return null; + } + } + + public void checkIfActive() { + final ODatabaseRecordThreadLocal tl = ODatabaseRecordThreadLocal.INSTANCE; + final ODatabaseDocumentInternal currentDatabase = tl != null ? tl.get() : null; + if (currentDatabase != this) + throw new IllegalStateException( + "The current database instance (" + toString() + ") is not active on the current thread (" + Thread.currentThread() + + "). Current active database is: " + currentDatabase); + } + + @Override + public int addBlobCluster(final String iClusterName, final Object... iParameters) { + int id; + if (getStorage() instanceof OStorageProxy) { + id = command(new OCommandSQL("create blob cluster :1")).execute(iClusterName); + getMetadata().getSchema().reload(); + } else { + if (!existsCluster(iClusterName)) { + id = addCluster(iClusterName, iParameters); + } else + id = getClusterIdByName(iClusterName); + getMetadata().getSchema().addBlobCluster(id); + } + return id; + } + + public Set getBlobClusterIds() { + return getMetadata().getSchema().getBlobClusters(); + } + + private class UncompletedCommit implements OUncompletedCommit { + + private final boolean topLevel; + private final OUncompletedCommit nestedCommit; + + public UncompletedCommit(boolean topLevel, OUncompletedCommit nestedCommit) { + this.topLevel = topLevel; + this.nestedCommit = nestedCommit; + } + + @Override + public Void complete() { + checkOpeness(); + checkIfActive(); + + if (!topLevel) { + nestedCommit.complete(); + return null; + } + + try { + nestedCommit.complete(); + } catch (RuntimeException e) { + OLogManager.instance().debug(this, "Error on transaction commit", e); + + // WAKE UP ROLLBACK LISTENERS + for (ODatabaseListener listener : browseListeners()) + try { + listener.onBeforeTxRollback(ODatabaseDocumentTxOrig.this); + } catch (Throwable t) { + OLogManager.instance().error(this, "Error before transaction rollback", t); + } + + // ROLLBACK TX AT DB LEVEL + nestedCommit.rollback(); + getLocalCache().clear(); + + // WAKE UP ROLLBACK LISTENERS + for (ODatabaseListener listener : browseListeners()) + try { + listener.onAfterTxRollback(ODatabaseDocumentTxOrig.this); + } catch (Throwable t) { + OLogManager.instance().error(this, "Error after transaction rollback", t); + } + throw e; + } + + // WAKE UP LISTENERS + for (ODatabaseListener listener : browseListeners()) + try { + listener.onAfterTxCommit(ODatabaseDocumentTxOrig.this); + } catch (Exception e) { + final String message = + "Error after the transaction has been committed. The transaction remains valid. The exception caught was on execution of " + + listener.getClass() + ".onAfterTxCommit()"; + + OLogManager.instance().error(this, message, e); + + throw OException.wrapException(new OTransactionBlockedException(message), e); + } + + return null; + } + + @Override + public void rollback() { + checkOpeness(); + checkIfActive(); + + if (!topLevel) { + nestedCommit.rollback(); + return; + } + + // WAKE UP ROLLBACK LISTENERS + for (ODatabaseListener listener : browseListeners()) + try { + listener.onBeforeTxRollback(ODatabaseDocumentTxOrig.this); + } catch (Throwable t) { + OLogManager.instance().error(this, "Error before transaction rollback", t); + } + + // ROLLBACK TX AT DB LEVEL + nestedCommit.rollback(); + getLocalCache().clear(); + + // WAKE UP ROLLBACK LISTENERS + for (ODatabaseListener listener : browseListeners()) + try { + listener.onAfterTxRollback(ODatabaseDocumentTxOrig.this); + } catch (Throwable t) { + OLogManager.instance().error(this, "Error after transaction rollback", t); + } + } + } + + private void compileHooks() { + final List[] intermediateHooksByScope = new List[ORecordHook.SCOPE.values().length]; + for (ORecordHook.SCOPE scope : ORecordHook.SCOPE.values()) + intermediateHooksByScope[scope.ordinal()] = new ArrayList<>(); + + for (ORecordHook hook : hooks.keySet()) + for (ORecordHook.SCOPE scope : hook.getScopes()) + intermediateHooksByScope[scope.ordinal()].add(hook); + + for (ORecordHook.SCOPE scope : ORecordHook.SCOPE.values()) { + final int ordinal = scope.ordinal(); + final List scopeHooks = intermediateHooksByScope[ordinal]; + hooksByScope[ordinal] = scopeHooks.toArray(new ORecordHook[scopeHooks.size()]); + } + } + + @Override + public OSharedContext getSharedContext() { + // NOW NEED TO GET THE CONTEXT FROM RESOURCES IN FUTURE WILL BE NOT NEEDED + if (sharedContext == null) { + sharedContext = storage.getResource(OSharedContext.class.getName(), new Callable() { + @Override + public OSharedContext call() throws Exception { + throw new ODatabaseException("Accessing to the database context before the database has bean initialized"); + } + }); + } + return sharedContext; + } + + public OTodoResultSet query(String query, Object... args) { + return getStorage().query(this, query, args); + } + + public OTodoResultSet query(String query, Map args) { + return getStorage().query(this, query, args); + } + + public OTodoResultSet command(String query, Object... args) { + return getStorage().command(this, query, args); + } + + public OTodoResultSet command(String query, Map args) { + return getStorage().command(this, query, args); + } + + public static Object executeWithRetries(final OCallable callback, final int maxRetry) { + return executeWithRetries(callback, maxRetry, 0); + } + + public static Object executeWithRetries(final OCallable callback, final int maxRetry, + final int waitBetweenRetry) { + ONeedRetryException lastException = null; + for (int retry = 0; retry < maxRetry; ++retry) { + try { + return callback.call(retry); + } catch (ONeedRetryException e) { + // SAVE LAST EXCEPTION AND RETRY + lastException = e; + + if (waitBetweenRetry > 0) + try { + Thread.sleep(waitBetweenRetry); + } catch (InterruptedException e1) { + Thread.currentThread().interrupt(); + break; + } + } + } + throw lastException; + } + + private void bindPropertiesToContext(OContextConfiguration configuration, final Map iProperties) { + final String connectionStrategy = iProperties != null ? (String) iProperties.get("connectionStrategy") : null; + if (connectionStrategy != null) + configuration.setValue(OGlobalConfiguration.CLIENT_CONNECTION_STRATEGY, connectionStrategy); + + final String compressionMethod = iProperties != null ? + (String) iProperties.get(OGlobalConfiguration.STORAGE_COMPRESSION_METHOD.getKey().toLowerCase()) : + null; + if (compressionMethod != null) + // SAVE COMPRESSION METHOD IN CONFIGURATION + configuration.setValue(OGlobalConfiguration.STORAGE_COMPRESSION_METHOD, compressionMethod); + + final String encryptionMethod = iProperties != null ? + (String) iProperties.get(OGlobalConfiguration.STORAGE_ENCRYPTION_METHOD.getKey().toLowerCase()) : + null; + if (encryptionMethod != null) + // SAVE ENCRYPTION METHOD IN CONFIGURATION + configuration.setValue(OGlobalConfiguration.STORAGE_ENCRYPTION_METHOD, encryptionMethod); + + final String encryptionKey = + iProperties != null ? (String) iProperties.get(OGlobalConfiguration.STORAGE_ENCRYPTION_KEY.getKey().toLowerCase()) : null; + if (encryptionKey != null) + // SAVE ENCRYPTION KEY IN CONFIGURATION + configuration.setValue(OGlobalConfiguration.STORAGE_ENCRYPTION_KEY, encryptionKey); + } + + private void bindPropertiesToContextGlobal(OContextConfiguration configuration, + final Map iProperties) { + final String connectionStrategy = iProperties != null ? (String) iProperties.get("connectionStrategy") : null; + if (connectionStrategy != null) + configuration.setValue(OGlobalConfiguration.CLIENT_CONNECTION_STRATEGY, connectionStrategy); + + final String compressionMethod = + iProperties != null ? (String) iProperties.get(OGlobalConfiguration.STORAGE_COMPRESSION_METHOD) : null; + if (compressionMethod != null) + // SAVE COMPRESSION METHOD IN CONFIGURATION + configuration.setValue(OGlobalConfiguration.STORAGE_COMPRESSION_METHOD, compressionMethod); + + final String encryptionMethod = + iProperties != null ? (String) iProperties.get(OGlobalConfiguration.STORAGE_ENCRYPTION_METHOD) : null; + if (encryptionMethod != null) + // SAVE ENCRYPTION METHOD IN CONFIGURATION + configuration.setValue(OGlobalConfiguration.STORAGE_ENCRYPTION_METHOD, encryptionMethod); + + final String encryptionKey = iProperties != null ? (String) iProperties.get(OGlobalConfiguration.STORAGE_ENCRYPTION_KEY) : null; + if (encryptionKey != null) + // SAVE ENCRYPTION KEY IN CONFIGURATION + configuration.setValue(OGlobalConfiguration.STORAGE_ENCRYPTION_KEY, encryptionKey); + } + +} \ No newline at end of file diff --git a/core/src/main/java/com/orientechnologies/orient/core/db/document/ODatabaseDocumentTxPooled.java b/core/src/main/java/com/orientechnologies/orient/core/db/document/ODatabaseDocumentTxPooled.java index 372d64f1c14..71f52959457 100755 --- a/core/src/main/java/com/orientechnologies/orient/core/db/document/ODatabaseDocumentTxPooled.java +++ b/core/src/main/java/com/orientechnologies/orient/core/db/document/ODatabaseDocumentTxPooled.java @@ -145,12 +145,12 @@ public void forceClose() { super.close(); } - @Override +// @Override protected void checkOpeness() { if (ownerPool == null) throw new ODatabaseException( "Database instance has been released to the pool. Get another database instance from the pool with the right username and password"); - super.checkOpeness(); +// super.checkOpeness(); } } diff --git a/core/src/test/java/com/orientechnologies/orient/core/sql/OCommandExecutorSQLDropPropertyTest.java b/core/src/test/java/com/orientechnologies/orient/core/sql/OCommandExecutorSQLDropPropertyTest.java index 83e42291de5..1684f46722d 100755 --- a/core/src/test/java/com/orientechnologies/orient/core/sql/OCommandExecutorSQLDropPropertyTest.java +++ b/core/src/test/java/com/orientechnologies/orient/core/sql/OCommandExecutorSQLDropPropertyTest.java @@ -21,6 +21,7 @@ import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx; import com.orientechnologies.orient.core.metadata.schema.OClass; +import com.orientechnologies.orient.core.metadata.schema.OSchema; import com.orientechnologies.orient.core.metadata.schema.OSchemaProxy; import com.orientechnologies.orient.core.metadata.schema.OType; import org.junit.Assert; @@ -42,7 +43,7 @@ public static void beforeClass() throws Exception { @Test public void test() { - OSchemaProxy schema = db.getMetadata().getSchema(); + OSchema schema = db.getMetadata().getSchema(); OClass foo = schema.createClass("Foo"); foo.createProperty("name", OType.STRING); diff --git a/core/src/test/java/com/orientechnologies/orient/core/tx/DuplicateDictionaryIndexChangesTxTest.java b/core/src/test/java/com/orientechnologies/orient/core/tx/DuplicateDictionaryIndexChangesTxTest.java index ac3414510fd..80f5c0d05a2 100644 --- a/core/src/test/java/com/orientechnologies/orient/core/tx/DuplicateDictionaryIndexChangesTxTest.java +++ b/core/src/test/java/com/orientechnologies/orient/core/tx/DuplicateDictionaryIndexChangesTxTest.java @@ -36,7 +36,7 @@ public class DuplicateDictionaryIndexChangesTxTest { @BeforeClass public static void before() { - db = new ODatabaseDocumentTx("memory:" + DuplicateDictionaryIndexChangesTxTest.class.getSimpleName(), false, true); + db = new ODatabaseDocumentTx("memory:" + DuplicateDictionaryIndexChangesTxTest.class.getSimpleName()); } @AfterClass diff --git a/core/src/test/java/com/orientechnologies/orient/core/tx/DuplicateNonUniqueIndexChangesTxTest.java b/core/src/test/java/com/orientechnologies/orient/core/tx/DuplicateNonUniqueIndexChangesTxTest.java index b8c2c18b56c..eace35895b5 100644 --- a/core/src/test/java/com/orientechnologies/orient/core/tx/DuplicateNonUniqueIndexChangesTxTest.java +++ b/core/src/test/java/com/orientechnologies/orient/core/tx/DuplicateNonUniqueIndexChangesTxTest.java @@ -41,7 +41,7 @@ public class DuplicateNonUniqueIndexChangesTxTest { @BeforeClass public static void before() { - db = new ODatabaseDocumentTx("memory:" + DuplicateNonUniqueIndexChangesTxTest.class.getSimpleName(), false, true); + db = new ODatabaseDocumentTx("memory:" + DuplicateNonUniqueIndexChangesTxTest.class.getSimpleName()); } @AfterClass diff --git a/core/src/test/java/com/orientechnologies/orient/core/tx/DuplicateUniqueIndexChangesTxTest.java b/core/src/test/java/com/orientechnologies/orient/core/tx/DuplicateUniqueIndexChangesTxTest.java index 901eadcfb39..b02042a95c3 100644 --- a/core/src/test/java/com/orientechnologies/orient/core/tx/DuplicateUniqueIndexChangesTxTest.java +++ b/core/src/test/java/com/orientechnologies/orient/core/tx/DuplicateUniqueIndexChangesTxTest.java @@ -37,7 +37,7 @@ public class DuplicateUniqueIndexChangesTxTest { @BeforeClass public static void before() { - db = new ODatabaseDocumentTx("memory:" + DuplicateUniqueIndexChangesTxTest.class.getSimpleName(), false, true); + db = new ODatabaseDocumentTx("memory:" + DuplicateUniqueIndexChangesTxTest.class.getSimpleName()); } @AfterClass diff --git a/graphdb/src/main/java/com/tinkerpop/blueprints/impls/orient/OrientVertex.java b/graphdb/src/main/java/com/tinkerpop/blueprints/impls/orient/OrientVertex.java index 6af63922cd1..b749250712f 100755 --- a/graphdb/src/main/java/com/tinkerpop/blueprints/impls/orient/OrientVertex.java +++ b/graphdb/src/main/java/com/tinkerpop/blueprints/impls/orient/OrientVertex.java @@ -1045,7 +1045,7 @@ protected String[] getFieldNames(final Direction iDirection, String... iClassNam { return null; } - OSchemaProxy schema = getGraph().getRawGraph().getMetadata().getSchema(); + OSchema schema = getGraph().getRawGraph().getMetadata().getSchema(); Set allClassNames = new HashSet(); for (String className : iClassNames) { diff --git a/tests/src/test/java/com/orientechnologies/orient/test/database/auto/SQLSelectTest.java b/tests/src/test/java/com/orientechnologies/orient/test/database/auto/SQLSelectTest.java index 3df426ea335..a8b6fdd1b36 100755 --- a/tests/src/test/java/com/orientechnologies/orient/test/database/auto/SQLSelectTest.java +++ b/tests/src/test/java/com/orientechnologies/orient/test/database/auto/SQLSelectTest.java @@ -1675,7 +1675,7 @@ public void testMultipleClustersWithPagination() throws Exception { @Test public void testOutFilterInclude() { - OSchemaProxy schema = database.getMetadata().getSchema(); + OSchema schema = database.getMetadata().getSchema(); schema.createClass("TestOutFilterInclude", schema.getClass("V")); database.command(new OCommandSQL("create class linkedToOutFilterInclude extends E")).execute(); database.command(new OCommandSQL("insert into TestOutFilterInclude content { \"name\": \"one\" }")).execute(); @@ -1733,7 +1733,7 @@ public void testBinaryClusterSelect() { @Test public void testExpandSkip() { - OSchemaProxy schema = database.getMetadata().getSchema(); + OSchema schema = database.getMetadata().getSchema(); OClass v = schema.getClass("V"); final OClass cls = schema.createClass("TestExpandSkip", v); cls.createProperty("name", OType.STRING); @@ -1775,7 +1775,7 @@ public void testExpandSkip() { @Test public void testPolymorphicEdges() { - OSchemaProxy schema = database.getMetadata().getSchema(); + OSchema schema = database.getMetadata().getSchema(); OClass v = schema.getClass("V"); OClass e = schema.getClass("E"); final OClass v1 = schema.createClass("TestPolymorphicEdges_V", v); @@ -1803,7 +1803,7 @@ public void testPolymorphicEdges() { @Test public void testSizeOfLink() { - OSchemaProxy schema = database.getMetadata().getSchema(); + OSchema schema = database.getMetadata().getSchema(); OClass v = schema.getClass("V"); final OClass cls = schema.createClass("TestSizeOfLink", v); database.command(new OCommandSQL("CREATE VERTEX TestSizeOfLink set name = '1'")).execute(); @@ -1818,7 +1818,7 @@ public void testSizeOfLink() { @Test public void testEmbeddedMapAndDotNotation() { - OSchemaProxy schema = database.getMetadata().getSchema(); + OSchema schema = database.getMetadata().getSchema(); OClass v = schema.getClass("V"); final OClass cls = schema.createClass("EmbeddedMapAndDotNotation", v); database.command(new OCommandSQL("CREATE VERTEX EmbeddedMapAndDotNotation set name = 'foo'")).execute(); @@ -1840,7 +1840,7 @@ public void testEmbeddedMapAndDotNotation() { @Test public void testLetWithQuotedValue() { - OSchemaProxy schema = database.getMetadata().getSchema(); + OSchema schema = database.getMetadata().getSchema(); OClass v = schema.getClass("V"); final OClass cls = schema.createClass("LetWithQuotedValue", v); database.command(new OCommandSQL("CREATE VERTEX LetWithQuotedValue set name = \"\\\"foo\\\"\"")).execute();