All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.ravendb.client.documents.DocumentStore Maven / Gradle / Ivy

There is a newer version: 6.0.1
Show newest version
package net.ravendb.client.documents;

import net.ravendb.client.documents.changes.DatabaseChanges;
import net.ravendb.client.documents.changes.DatabaseChangesOptions;
import net.ravendb.client.documents.changes.EvictItemsFromCacheBasedOnChanges;
import net.ravendb.client.documents.changes.IDatabaseChanges;
import net.ravendb.client.documents.identity.MultiDatabaseHiLoIdGenerator;
import net.ravendb.client.documents.operations.MaintenanceOperationExecutor;
import net.ravendb.client.documents.operations.OperationExecutor;
import net.ravendb.client.documents.session.DocumentSession;
import net.ravendb.client.documents.session.IDocumentSession;
import net.ravendb.client.documents.session.SessionOptions;
import net.ravendb.client.documents.smuggler.DatabaseSmuggler;
import net.ravendb.client.http.AggressiveCacheMode;
import net.ravendb.client.http.AggressiveCacheOptions;
import net.ravendb.client.http.RequestExecutor;
import net.ravendb.client.primitives.*;
import org.apache.commons.lang3.ObjectUtils;

import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.*;
import java.util.function.Supplier;

/**
 * Manages access to RavenDB and open sessions to work with RavenDB.
 */
public class DocumentStore extends DocumentStoreBase {

    private ExecutorService executorService = Executors.newCachedThreadPool();

    private final ConcurrentMap _databaseChanges = new ConcurrentHashMap<>();

    private final ConcurrentMap> _aggressiveCacheChanges = new ConcurrentHashMap<>();

    private final ConcurrentMap> requestExecutors = new ConcurrentSkipListMap<>(String.CASE_INSENSITIVE_ORDER);

    private MultiDatabaseHiLoIdGenerator _multiDbHiLo;

    private MaintenanceOperationExecutor maintenanceOperationExecutor;
    private OperationExecutor operationExecutor;

    private DatabaseSmuggler _smuggler;

    private String identifier;

    public DocumentStore(String url, String database) {
        this.setUrls(new String[]{url});
        this.setDatabase(database);
    }

    public DocumentStore(String[] urls, String database) {
        this.setUrls(urls);
        this.setDatabase(database);
    }

    public DocumentStore() {

    }

    public ExecutorService getExecutorService() {
        return executorService;
    }

    /**
     * Gets the identifier for this store.
     */
    public String getIdentifier() {
        if (identifier != null) {
            return identifier;
        }

        if (urls == null) {
            return null;
        }

        if (database != null) {
            return String.join(",", urls) + " (DB: " + database + ")";
        }

        return String.join(",", urls);
    }

    /**
     * Sets the identifier for this store.
     */
    public void setIdentifier(String identifier) {
        this.identifier = identifier;
    }

    @SuppressWarnings("EmptyTryBlock")
    public void close() {
        EventHelper.invoke(beforeClose, this, EventArgs.EMPTY);

        for (Lazy value : _aggressiveCacheChanges.values()) {
            if (!value.isValueCreated()) {
                continue;
            }

            value.getValue().close();
        }

        for (IDatabaseChanges changes : _databaseChanges.values()) {
            try (CleanCloseable value = changes) {
                // try will close all values
            }
        }

        if (_multiDbHiLo != null) {
            try {
                _multiDbHiLo.returnUnusedRange();
            } catch (Exception e) {
                // ignore
            }
        }

        if (subscriptions() != null) {
            subscriptions().close();
        }

        disposed = true;

        EventHelper.invoke(new ArrayList<>(afterClose), this, EventArgs.EMPTY);

        for (Map.Entry> kvp : requestExecutors.entrySet()) {
            if (!kvp.getValue().isValueCreated()) {
                continue;
            }

            kvp.getValue().getValue().close();
        }

        executorService.shutdown();
    }

    /**
     * Opens the session.
     */
    @Override
    public IDocumentSession openSession() {
        return openSession(new SessionOptions());
    }

    /**
     * Opens the session for a particular database
     */
    @Override
    public IDocumentSession openSession(String database) {
        SessionOptions sessionOptions = new SessionOptions();
        sessionOptions.setDatabase(database);

        return openSession(sessionOptions);
    }

    @Override
    public IDocumentSession openSession(SessionOptions options) {
        assertInitialized();
        ensureNotClosed();

        UUID sessionId = UUID.randomUUID();
        DocumentSession session = new DocumentSession(this, sessionId, options);
        registerEvents(session);
        afterSessionCreated(session);
        return session;
    }

    @Override
    public RequestExecutor getRequestExecutor() {
        return getRequestExecutor(null);
    }

    @Override
    public RequestExecutor getRequestExecutor(String database) {
        assertInitialized();

        if (database == null) {
            database = getDatabase();
        }

        Lazy executor = requestExecutors.get(database);
        if (executor != null) {
            return executor.getValue();
        }

        final String effectiveDatabase = database;

        Supplier createRequestExecutor = () -> {
            RequestExecutor requestExecutor = RequestExecutor.create(getUrls(), effectiveDatabase, getCertificate(), getCertificatePrivateKeyPassword(), getTrustStore(), executorService, getConventions());
            registerEvents(requestExecutor);

            return requestExecutor;
        };

        Supplier createRequestExecutorForSingleNode = () -> {
            RequestExecutor forSingleNode = RequestExecutor.createForSingleNodeWithConfigurationUpdates(getUrls()[0], effectiveDatabase, getCertificate(), getCertificatePrivateKeyPassword(), getTrustStore(), executorService, getConventions());
            registerEvents(forSingleNode);

            return forSingleNode;
        };

        if (!getConventions().isDisableTopologyUpdates()) {
            executor = new Lazy<>(createRequestExecutor);
        } else {
            executor = new Lazy<>(createRequestExecutorForSingleNode);
        }

        requestExecutors.put(database, executor);

        return executor.getValue();
    }

    @Override
    public CleanCloseable setRequestTimeout(Duration timeout) {
        return setRequestTimeout(timeout, null);
    }

    @Override
    public CleanCloseable setRequestTimeout(Duration timeout, String database) {
        assertInitialized();

        database = ObjectUtils.firstNonNull(database, getDatabase());

        if (database == null) {
            throw new IllegalStateException("Cannot use setRequestTimeout without a default database defined "
            + "unless 'database' parameter is provided. Did you forget to pass 'database' parameter?");
        }

        RequestExecutor requestExecutor = getRequestExecutor(database);
        Duration oldTimeout = requestExecutor.getDefaultTimeout();
        requestExecutor.setDefaultTimeout(timeout);

        return () -> requestExecutor.setDefaultTimeout(oldTimeout);
    }

    /**
     * Initializes this instance.
     */
    @Override
    public IDocumentStore initialize() {
        if (initialized) {
            return this;
        }

        assertValidConfiguration();

        RequestExecutor.validateUrls(urls, getCertificate());

        try {
            if (getConventions().getDocumentIdGenerator() == null) { // don't overwrite what the user is doing
                MultiDatabaseHiLoIdGenerator generator = new MultiDatabaseHiLoIdGenerator(this, getConventions());
                _multiDbHiLo = generator;

                getConventions().setDocumentIdGenerator(generator::generateDocumentId);
            }

            getConventions().freeze();
            initialized = true;
        } catch (Exception e) {
            close();
            throw ExceptionsUtils.unwrapException(e);
        }

        return this;
    }


    /**
     * Validate the configuration for the document store
     */
    protected void assertValidConfiguration() {
        if (urls == null || urls.length == 0) {
            throw new IllegalArgumentException("Document store URLs cannot be empty");
        }
    }

    /**
     * Setup the context for no aggressive caching
     * 

* This is mainly useful for internal use inside RavenDB, when we are executing * queries that have been marked with WaitForNonStaleResults, we temporarily disable * aggressive caching. */ public CleanCloseable disableAggressiveCaching() { return disableAggressiveCaching(null); } /** * Setup the context for no aggressive caching *

* This is mainly useful for internal use inside RavenDB, when we are executing * queries that have been marked with WaitForNonStaleResults, we temporarily disable * aggressive caching. */ public CleanCloseable disableAggressiveCaching(String databaseName) { assertInitialized(); RequestExecutor re = getRequestExecutor(ObjectUtils.firstNonNull(databaseName, getDatabase())); AggressiveCacheOptions old = re.aggressiveCaching.get(); re.aggressiveCaching.set(null); return () -> re.aggressiveCaching.set(old); } @Override public IDatabaseChanges changes() { return changes(null, null); } @Override public IDatabaseChanges changes(String database) { return changes(database, null); } @Override public IDatabaseChanges changes(String database, String nodeTag) { assertInitialized(); DatabaseChangesOptions changesOptions = new DatabaseChangesOptions(ObjectUtils.firstNonNull(database, getDatabase()), nodeTag); return _databaseChanges.computeIfAbsent(changesOptions, this::createDatabaseChanges); } protected IDatabaseChanges createDatabaseChanges(DatabaseChangesOptions node) { return new DatabaseChanges(getRequestExecutor(node.getDatabaseName()), node.getDatabaseName(), executorService, () -> _databaseChanges.remove(node), node.getNodeTag()); } public Exception getLastDatabaseChangesStateException() { return getLastDatabaseChangesStateException(null, null); } public Exception getLastDatabaseChangesStateException(String database) { return getLastDatabaseChangesStateException(database, null); } public Exception getLastDatabaseChangesStateException(String database, String nodeTag) { DatabaseChangesOptions node = new DatabaseChangesOptions(ObjectUtils.firstNonNull(database, getDatabase()), nodeTag); DatabaseChanges databaseChanges = (DatabaseChanges) _databaseChanges.get(node); if (databaseChanges != null) { return databaseChanges.getLastConnectionStateException(); } return null; } @Override public CleanCloseable aggressivelyCacheFor(Duration cacheDuration) { return aggressivelyCacheFor(cacheDuration, getConventions().aggressiveCache().getMode(), null); } @Override public CleanCloseable aggressivelyCacheFor(Duration cacheDuration, String database) { return aggressivelyCacheFor(cacheDuration, getConventions().aggressiveCache().getMode(), database); } @Override public CleanCloseable aggressivelyCacheFor(Duration cacheDuration, AggressiveCacheMode mode) { return aggressivelyCacheFor(cacheDuration, mode, null); } @Override public CleanCloseable aggressivelyCacheFor(Duration cacheDuration, AggressiveCacheMode mode, String database) { assertInitialized(); database = ObjectUtils.firstNonNull(database, getDatabase()); if (database == null) { throw new IllegalStateException("Cannot use aggressivelyCache and aggressivelyCacheFor without a default database defined " + "unless 'database' parameter is provided. Did you forget to pass 'database' parameter?"); } if (mode != AggressiveCacheMode.DO_NOT_TRACK_CHANGES) { listenToChangesAndUpdateTheCache(database); } RequestExecutor re = getRequestExecutor(database); AggressiveCacheOptions old = re.aggressiveCaching.get(); AggressiveCacheOptions newOptions = new AggressiveCacheOptions(cacheDuration, mode); re.aggressiveCaching.set(newOptions); return () -> re.aggressiveCaching.set(old); } private void listenToChangesAndUpdateTheCache(String database) { Lazy lazy = _aggressiveCacheChanges.get(database); if (lazy == null) { lazy = _aggressiveCacheChanges.computeIfAbsent(database, db -> new Lazy<>(() -> new EvictItemsFromCacheBasedOnChanges(this, database))); } lazy.getValue(); // force evaluation } private final List> afterClose = new ArrayList<>(); private final List> beforeClose = new ArrayList<>(); public void addBeforeCloseListener(EventHandler event) { this.beforeClose.add(event); } @Override public void removeBeforeCloseListener(EventHandler event) { this.beforeClose.remove(event); } public void addAfterCloseListener(EventHandler event) { this.afterClose.add(event); } @Override public void removeAfterCloseListener(EventHandler event) { this.afterClose.remove(event); } @Override public DatabaseSmuggler smuggler() { if (_smuggler == null) { _smuggler = new DatabaseSmuggler(this); } return _smuggler; } @Override public MaintenanceOperationExecutor maintenance() { assertInitialized(); if (maintenanceOperationExecutor == null) { maintenanceOperationExecutor = new MaintenanceOperationExecutor(this); } return maintenanceOperationExecutor; } @Override public OperationExecutor operations() { if (operationExecutor == null) { operationExecutor = new OperationExecutor(this); } return operationExecutor; } @Override public BulkInsertOperation bulkInsert() { return bulkInsert(null); } @Override public BulkInsertOperation bulkInsert(String database) { assertInitialized(); return new BulkInsertOperation(ObjectUtils.firstNonNull(database, getDatabase()), this); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy