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

org.infinispan.schematic.internal.CacheSchematicDb Maven / Gradle / Ivy

Go to download

Module for storing JSON/BSON documents and JSON Schemas in Infinispan

There is a newer version: 5.4.1.Final
Show newest version
/*
 * ModeShape (http://www.modeshape.org)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.infinispan.schematic.internal;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicReference;
import org.infinispan.Cache;
import org.infinispan.commons.util.concurrent.FutureListener;
import org.infinispan.commons.util.concurrent.NotifyingFuture;
import org.infinispan.distexec.mapreduce.Collector;
import org.infinispan.distexec.mapreduce.MapReduceTask;
import org.infinispan.schematic.SchemaLibrary;
import org.infinispan.schematic.SchemaLibrary.Results;
import org.infinispan.schematic.Schematic;
import org.infinispan.schematic.SchematicDb;
import org.infinispan.schematic.SchematicEntry;
import org.infinispan.schematic.SchematicEntry.FieldName;
import org.infinispan.schematic.document.Binary;
import org.infinispan.schematic.document.Document;
import org.infinispan.schematic.document.JsonSchema;

public class CacheSchematicDb implements SchematicDb {

    private final String name;
    private final Cache store;
    private final CacheContext context;
    private final AtomicReference schemaLibrary = new AtomicReference();
    private final String schemaCacheName;
    private String defaultContentTypeForDocument;
    private String defaultContentTypeForBinary;
    private String defaultSchemaUri;
    private String description;

    public CacheSchematicDb( Cache store ) {
        this.name = store.getName();
        this.store = store;
        String defaultContentTypeForDocs = Schematic.ContentTypes.JSON;
        String defaultContentTypeForBinary = Schematic.ContentTypes.BINARY;
        String defaultSchemaUri = JsonSchema.Version.Latest.CORE_METASCHEMA_URL;
        String description = "";
        String schemaCacheName = store.getName() + "Schemas";

        // Load the database document from the cache ...
        SchematicEntry databaseDocument = store.get("");
        if (databaseDocument != null && databaseDocument.hasDocumentContent()) {
            Document dbDoc = databaseDocument.getContentAsDocument();
            defaultContentTypeForDocs = dbDoc.getString("defaultContentTypeForDocuments", defaultContentTypeForDocs);
            defaultContentTypeForBinary = dbDoc.getString("defaultContentTypeForBinary", defaultContentTypeForBinary);
            defaultSchemaUri = dbDoc.getString("defaultSchemaUri", defaultSchemaUri);
            description = dbDoc.getString("description", description);
            schemaCacheName = dbDoc.getString("schemaCacheName", schemaCacheName);
        }

        this.defaultContentTypeForBinary = defaultContentTypeForBinary;
        this.defaultContentTypeForDocument = defaultContentTypeForDocs;
        this.defaultSchemaUri = defaultSchemaUri;
        this.description = description;
        this.schemaCacheName = schemaCacheName;
        this.context = new CacheContext(store.getAdvancedCache());
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public String getDescription() {
        return description;
    }

    @Override
    public Cache getCache() {
        return this.store;
    }

    @Override
    public void start() {
        this.store.start();
    }

    @Override
    public void stop() {
        this.store.stop();
        CacheSchemaLibrary schemaLibrary = schemaLibrary(false);
        if (schemaLibrary != null) schemaLibrary.stop();
    }

    @Override
    public SchemaLibrary getSchemaLibrary() {
        return schemaLibrary(true);
    }

    protected CacheSchemaLibrary schemaLibrary( boolean createIfMissing ) {
        CacheSchemaLibrary schemaLibrary = this.schemaLibrary.get();
        if (schemaLibrary == null && createIfMissing) {
            // Now get the cache for the schema and create the library ...
            Cache schemaStore = this.store.getCacheManager().getCache(schemaCacheName);
            schemaLibrary = new CacheSchemaLibrary(schemaStore);
            if (this.schemaLibrary.compareAndSet(null, schemaLibrary)) {
                schemaLibrary.start();
            } else {
                // Someone else snuck in and set the value ...
                schemaLibrary = this.schemaLibrary.get();
            }
        }
        return schemaLibrary;
    }

    @Override
    public Map validateAll() {
        CacheSchemaLibrary schemaLibrary = schemaLibrary(true);
        if (store.getAdvancedCache().getRpcManager() == null) {
            // This is a non-clustered cache, which cannot run Map-Reduce. In this case, just go through them
            // all and run validation manually using the mapper...
            DocumentValidationMapper mapper = new DocumentValidationMapper(schemaLibrary, defaultSchemaUri);
            ResultsCollector resultsCollector = new ResultsCollector();
            for (Map.Entry entry : store.entrySet()) {
                String key = entry.getKey();
                SchematicEntry value = entry.getValue();
                mapper.map(key, value, resultsCollector);
            }
            return resultsCollector.getResultsByKey();
        }

        // It is a clustered cache, so we can run Map-Reduce ...

        // Create a copy of all of the JSON Schema documents ...
        InMemoryDocumentLibrary schemaDocs = new InMemoryDocumentLibrary(schemaLibrary.getName());
        for (Map.Entry entry : schemaLibrary.store().entrySet()) {
            String key = entry.getKey();
            SchematicEntry value = entry.getValue();
            schemaDocs.put(key, value.getContentAsDocument());
        }

        // Now create the Map-Reduce task, using the copy of the JSON Schema library ...
        MapReduceTask task = new MapReduceTask(
                                                                                                                                 this.store);
        task.mappedWith(new DocumentValidationMapper(schemaDocs, defaultSchemaUri));
        task.reducedWith(new DocumentValidationReducer());

        // Now execute ...
        return task.execute();
    }

    @Override
    public Map validate( String firstKey,
                                          String... additionalKeys ) {
        Map resultsByKey = new HashMap();
        Results results = validate(firstKey);
        if (results != null && results.hasProblems()) {
            resultsByKey.put(firstKey, results);
        }
        for (String key : additionalKeys) {
            results = validate(key);
            if (results != null && results.hasProblems()) {
                resultsByKey.put(key, results);
            }
        }
        return resultsByKey;
    }

    @Override
    public Results validate( String key ) {
        SchematicEntry entry = store.get(key);
        if (entry != null) {
            DocumentValidationMapper mapper = new DocumentValidationMapper(schemaLibrary(true), defaultSchemaUri);
            return mapper.validate(key, entry); // might be null if no JSON document or doc has no affiliated schema
        }
        return null;
    }

    protected static class ResultsCollector implements Collector {
        private final Map resultsByKey = new HashMap();

        @Override
        public void emit( String key,
                          Results value ) {
            resultsByKey.put(key, value);
        }

        public Map getResultsByKey() {
            return resultsByKey;
        }
    }

    protected SchematicEntry proxy( String key,
                                    SchematicEntry entry ) {
        if (entry == null) return null;
        SchematicEntryLiteral literal = (SchematicEntryLiteral)entry;
        return literal.getProxy(context, key);
    }

    protected SchematicEntry removedResult( String key,
                                            SchematicEntry entry ) {
        SchematicEntryLiteral literal = (SchematicEntryLiteral)entry;
        literal.markRemoved(true);
        return proxy(key, literal);
    }

    protected SchematicEntryProxyFuture future( String key,
                                                NotifyingFuture future,
                                                boolean isRemoved ) {
        return new SchematicEntryProxyFuture(future, key, isRemoved);
    }

    @Override
    public SchematicEntry get( String key ) {
        return proxy(key, store.get(key));
    }

    @Override
    public boolean containsKey( String key ) {
        return store.containsKey(key);
    }

    @Override
    public SchematicEntry put( String key,
                               Document document,
                               Document metadata ) {
        if (metadata == null) metadata = Schematic.newDocument(FieldName.ID, key);
        SchematicEntry newEntry = new SchematicEntryLiteral(key, document, metadata, defaultContentTypeForDocument);
        SchematicEntry oldValue = store.put(key, newEntry);
        return oldValue != null ? removedResult(key, oldValue) : null;
    }

    @Override
    public SchematicEntry put( String key,
                               Binary binaryContent,
                               Document metadata ) {
        if (metadata == null) metadata = Schematic.newDocument(FieldName.ID, key);
        SchematicEntry newEntry = new SchematicEntryLiteral(key, binaryContent, metadata, defaultContentTypeForBinary);
        SchematicEntry oldValue = store.put(key, newEntry);
        return oldValue != null ? removedResult(key, oldValue) : null;
    }

    @Override
    public SchematicEntry put( Document entryDocument ) {
        Document metadata = entryDocument.getDocument(FieldName.METADATA);
        Object content = entryDocument.get(FieldName.CONTENT);
        if (metadata == null || content == null) {
            throw new IllegalArgumentException("The supplied document is not of the required format");
        }
        String key = metadata.getString(FieldName.ID);
        if (key == null) {
            throw new IllegalArgumentException("The supplied document is not of the required format");
        }
        SchematicEntry newEntry = null;
        if (content instanceof Document) {
            newEntry = new SchematicEntryLiteral(key, (Document)content, metadata, defaultContentTypeForDocument);
        } else {
            newEntry = new SchematicEntryLiteral(key, (Binary)content, metadata, defaultContentTypeForBinary);
        }
        SchematicEntry oldValue = store.put(key, newEntry);
        return oldValue != null ? removedResult(key, oldValue) : null;
    }

    @Override
    public SchematicEntry putIfAbsent( String key,
                                       Document document,
                                       Document metadata ) {
        if (metadata == null) metadata = Schematic.newDocument(FieldName.ID, key);
        SchematicEntryLiteral newEntry = new SchematicEntryLiteral(key, document, metadata, defaultContentTypeForDocument);
        SchematicEntry existingEntry = store.putIfAbsent(key, newEntry);
        if (existingEntry == null) return null;
        return proxy(key, existingEntry);
    }

    @Override
    public SchematicEntry putIfAbsent( String key,
                                       Binary binaryContent,
                                       Document metadata ) {
        if (metadata == null) metadata = Schematic.newDocument(FieldName.ID, key);
        SchematicEntryLiteral newEntry = new SchematicEntryLiteral(key, binaryContent, metadata, defaultContentTypeForBinary);
        SchematicEntry existingEntry = store.putIfAbsent(key, newEntry);
        if (existingEntry == null) return null;
        return proxy(key, existingEntry);
    }

    @Override
    public SchematicEntry putIfAbsent( Document entryDocument ) {
        Document metadata = entryDocument.getDocument(FieldName.METADATA);
        Object content = entryDocument.get(FieldName.CONTENT);
        if (metadata == null || content == null) {
            throw new IllegalArgumentException("The supplied document is not of the required format");
        }
        String key = metadata.getString(FieldName.ID);
        if (key == null) {
            throw new IllegalArgumentException("The supplied document is not of the required format");
        }
        SchematicEntry newEntry = null;
        if (content instanceof Document) {
            newEntry = new SchematicEntryLiteral(key, (Document)content, metadata, defaultContentTypeForDocument);
        } else {
            newEntry = new SchematicEntryLiteral(key, (Binary)content, metadata, defaultContentTypeForBinary);
        }
        SchematicEntry existingEntry = store.putIfAbsent(key, newEntry);
        if (existingEntry == null) return null;
        return proxy(key, existingEntry);
    }

    @Override
    public SchematicEntry replace( String key,
                                   Document document,
                                   Document metadata ) {
        if (metadata == null) metadata = Schematic.newDocument(FieldName.ID, key);
        SchematicEntryLiteral newEntry = new SchematicEntryLiteral(key, document, metadata, defaultContentTypeForDocument);
        return removedResult(key, store.replace(key, newEntry));
    }

    @Override
    public SchematicEntry replace( String key,
                                   Binary binaryContent,
                                   Document metadata ) {
        if (metadata == null) metadata = Schematic.newDocument(FieldName.ID, key);
        SchematicEntryLiteral newEntry = new SchematicEntryLiteral(key, binaryContent, metadata, defaultContentTypeForBinary);
        return removedResult(key, store.replace(key, newEntry));
    }

    @Override
    public SchematicEntry remove( String key ) {
        SchematicEntry existing = store.remove(key);
        return existing == null ? null : removedResult(key, existing);
    }

    @Override
    public boolean lock( Collection keys ) {
        if (context.isExplicitLockingEnabled() && !keys.isEmpty()) {
            return context.getCacheForLocking().lock(keys);
        }
        return true;
    }

    @Override
    public boolean isExplicitLockingEnabled() {
        return context.isExplicitLockingEnabled();
    }

    protected class SchematicEntryProxyFuture implements NotifyingFuture {
        private final NotifyingFuture original;
        private final String key;
        private final boolean isRemoved;

        protected SchematicEntryProxyFuture( NotifyingFuture original,
                                             String key,
                                             boolean isRemoved ) {
            this.original = original;
            this.key = key;
            this.isRemoved = isRemoved;
        }

        @Override
        public NotifyingFuture attachListener( final FutureListener listener ) {
            original.attachListener(listener);
            return this;
        }

        @Override
        public boolean cancel( boolean mayInterruptIfRunning ) {
            return original.cancel(mayInterruptIfRunning);
        }

        @Override
        public SchematicEntry get() throws InterruptedException, ExecutionException {
            SchematicEntry result = original.get();
            return isRemoved ? removedResult(key, result) : proxy(key, result);
        }

        @Override
        public SchematicEntry get( long timeout,
                                   TimeUnit unit ) throws InterruptedException, ExecutionException, TimeoutException {
            SchematicEntry result = original.get(timeout, unit);
            return isRemoved ? removedResult(key, result) : proxy(key, result);
        }

        @Override
        public boolean isCancelled() {
            return original.isCancelled();
        }

        @Override
        public boolean isDone() {
            return original.isDone();
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy