fr.inria.atlanmod.neoemf.data.blueprints.BlueprintsPersistenceBackendFactory.java Source code

Java tutorial

Introduction

Here is the source code for fr.inria.atlanmod.neoemf.data.blueprints.BlueprintsPersistenceBackendFactory.java

Source

/*
 * Copyright (c) 2013-2016 Atlanmod INRIA LINA Mines Nantes.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Atlanmod INRIA LINA Mines Nantes - initial API and implementation
 */

package fr.inria.atlanmod.neoemf.data.blueprints;

import com.tinkerpop.blueprints.Graph;
import com.tinkerpop.blueprints.GraphFactory;
import com.tinkerpop.blueprints.KeyIndexableGraph;
import com.tinkerpop.blueprints.impls.tg.TinkerGraph;

import fr.inria.atlanmod.neoemf.data.AbstractPersistenceBackendFactory;
import fr.inria.atlanmod.neoemf.data.InvalidDataStoreException;
import fr.inria.atlanmod.neoemf.data.PersistenceBackend;
import fr.inria.atlanmod.neoemf.data.PersistenceBackendFactory;
import fr.inria.atlanmod.neoemf.data.blueprints.config.InternalBlueprintsConfiguration;
import fr.inria.atlanmod.neoemf.data.blueprints.option.BlueprintsResourceOptions;
import fr.inria.atlanmod.neoemf.data.blueprints.option.BlueprintsStoreOptions;
import fr.inria.atlanmod.neoemf.data.blueprints.store.DirectWriteBlueprintsCacheManyStore;
import fr.inria.atlanmod.neoemf.data.blueprints.store.DirectWriteBlueprintsStore;
import fr.inria.atlanmod.neoemf.data.store.AutocommitStoreDecorator;
import fr.inria.atlanmod.neoemf.data.store.PersistentStore;
import fr.inria.atlanmod.neoemf.logging.NeoLogger;
import fr.inria.atlanmod.neoemf.option.PersistentStoreOptions;
import fr.inria.atlanmod.neoemf.resource.PersistentResource;

import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;

import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.MessageFormat;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;

import static com.google.common.base.Preconditions.checkArgument;
import static java.util.Objects.isNull;
import static java.util.Objects.nonNull;

public final class BlueprintsPersistenceBackendFactory extends AbstractPersistenceBackendFactory {

    public static final String NAME = BlueprintsPersistenceBackend.NAME;

    /**
     * The configuration file name.
     * <p/>
     * This file stores the metadata information about the underlying graph, i.e., graph type and other configuration
     * options.
     */
    private static final String BLUEPRINTS_CONFIG_FILE = "config.properties";

    private BlueprintsPersistenceBackendFactory() {
    }

    public static PersistenceBackendFactory getInstance() {
        return Holder.INSTANCE;
    }

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

    @Override
    protected PersistentStore createSpecificPersistentStore(PersistentResource resource, PersistenceBackend backend,
            Map<?, ?> options) throws InvalidDataStoreException {
        checkArgument(backend instanceof BlueprintsPersistenceBackend,
                "Trying to create a Graph-based EStore with an invalid backend");

        PersistentStore eStore;
        List<PersistentStoreOptions> storeOptions = getStoreOptions(options);

        // Store
        if (isNull(storeOptions) || storeOptions.isEmpty()
                || storeOptions.contains(BlueprintsStoreOptions.DIRECT_WRITE)
                || (storeOptions.size() == 1 && storeOptions.contains(BlueprintsStoreOptions.AUTOCOMMIT))) {
            eStore = new DirectWriteBlueprintsStore(resource, (BlueprintsPersistenceBackend) backend);
        } else if (storeOptions.contains(BlueprintsStoreOptions.CACHE_MANY)) {
            eStore = new DirectWriteBlueprintsCacheManyStore(resource, (BlueprintsPersistenceBackend) backend);
        } else {
            throw new InvalidDataStoreException("No valid base EStore found in the options");
        }
        // Autocommit
        if (nonNull(storeOptions) && storeOptions.contains(BlueprintsStoreOptions.AUTOCOMMIT)) {
            if (options.containsKey(BlueprintsResourceOptions.AUTOCOMMIT_CHUNK)) {
                int autoCommitChunk = Integer
                        .parseInt((String) options.get(BlueprintsResourceOptions.AUTOCOMMIT_CHUNK));
                eStore = new AutocommitStoreDecorator(eStore, autoCommitChunk);
            } else {
                eStore = new AutocommitStoreDecorator(eStore);
            }
        }
        return eStore;
    }

    @Override
    public PersistenceBackend createTransientBackend() {
        return new BlueprintsPersistenceBackend(new TinkerGraph());
    }

    @Override
    public BlueprintsPersistenceBackend createPersistentBackend(File file, Map<?, ?> options)
            throws InvalidDataStoreException {
        BlueprintsPersistenceBackend backend;
        PropertiesConfiguration configuration = null;

        try {
            configuration = getOrCreateBlueprintsConfiguration(file, options);

            try {
                Graph baseGraph = GraphFactory.open(configuration);

                if (baseGraph instanceof KeyIndexableGraph) {
                    backend = new BlueprintsPersistenceBackend((KeyIndexableGraph) baseGraph);
                } else {
                    NeoLogger.error("Graph type {0} does not support Key Indices", file.getAbsolutePath());
                    throw new InvalidDataStoreException(
                            "Graph type " + file.getAbsolutePath() + " does not support Key Indices");
                }
            } catch (RuntimeException e) {
                throw new InvalidDataStoreException(e);
            }
        } finally {
            if (nonNull(configuration)) {
                try {
                    configuration.save();
                } catch (ConfigurationException e) {
                    /*
                      * Unable to save configuration.
                    * Supposedly it's a minor error, so we log it without rising an exception.
                    */
                    NeoLogger.warn(e);
                }
            }
        }

        processGlobalConfiguration(file);

        return backend;
    }

    @Override
    public PersistentStore createTransientStore(PersistentResource resource, PersistenceBackend backend) {
        checkArgument(backend instanceof BlueprintsPersistenceBackend,
                "Trying to create a Graph-based EStore with an invalid backend");

        return new DirectWriteBlueprintsStore(resource, (BlueprintsPersistenceBackend) backend);
    }

    @Override
    public void copyBackend(PersistenceBackend from, PersistenceBackend to) {
        checkArgument(from instanceof BlueprintsPersistenceBackend && to instanceof BlueprintsPersistenceBackend,
                "Trying to use Graph backend copy on non Graph databases");

        BlueprintsPersistenceBackend source = (BlueprintsPersistenceBackend) from;
        BlueprintsPersistenceBackend target = (BlueprintsPersistenceBackend) to;

        source.copyTo(target);
    }

    private PropertiesConfiguration getOrCreateBlueprintsConfiguration(File directory, Map<?, ?> options)
            throws InvalidDataStoreException {
        PropertiesConfiguration configuration;

        // Try to load previous configurations
        Path path = Paths.get(directory.getAbsolutePath()).resolve(BLUEPRINTS_CONFIG_FILE);
        try {
            configuration = new PropertiesConfiguration(path.toFile());
        } catch (ConfigurationException e) {
            throw new InvalidDataStoreException(e);
        }

        // Initialize value if the config file has just been created
        if (!configuration.containsKey(BlueprintsResourceOptions.GRAPH_TYPE)) {
            configuration.setProperty(BlueprintsResourceOptions.GRAPH_TYPE,
                    BlueprintsResourceOptions.GRAPH_TYPE_DEFAULT);
        } else if (options.containsKey(BlueprintsResourceOptions.GRAPH_TYPE)) {
            // The file already existed, check that the issued options are not conflictive
            String savedGraphType = configuration.getString(BlueprintsResourceOptions.GRAPH_TYPE);
            String issuedGraphType = options.get(BlueprintsResourceOptions.GRAPH_TYPE).toString();
            if (!Objects.equals(savedGraphType, issuedGraphType)) {
                NeoLogger.error("Unable to create graph as type {0}, expected graph type was {1})", issuedGraphType,
                        savedGraphType);
                throw new InvalidDataStoreException("Unable to create graph as type " + issuedGraphType
                        + ", expected graph type was " + savedGraphType + ')');
            }
        }

        // Copy the options to the configuration
        for (Entry<?, ?> e : options.entrySet()) {
            configuration.setProperty(e.getKey().toString(), e.getValue().toString());
        }

        // Check we have a valid graph type, it is needed to get the graph name
        String graphType = configuration.getString(BlueprintsResourceOptions.GRAPH_TYPE);
        if (isNull(graphType)) {
            throw new InvalidDataStoreException("Graph type is undefined for " + directory.getAbsolutePath());
        }

        // Define the configuration
        String[] segments = graphType.split("\\.");
        if (segments.length >= 2) {
            String graphName = segments[segments.length - 2];
            String upperCaseGraphName = Character.toUpperCase(graphName.charAt(0)) + graphName.substring(1);
            String configClassName = MessageFormat.format("InternalBlueprints{0}Configuration", upperCaseGraphName);
            String configClassQualifiedName = MessageFormat
                    .format("fr.inria.atlanmod.neoemf.data.blueprints.{0}.config.{1}", graphName, configClassName);

            try {
                ClassLoader classLoader = BlueprintsPersistenceBackendFactory.class.getClassLoader();
                Class<?> configClass = classLoader.loadClass(configClassQualifiedName);
                Method configClassInstanceMethod = configClass.getMethod("getInstance");
                InternalBlueprintsConfiguration blueprintsConfig = (InternalBlueprintsConfiguration) configClassInstanceMethod
                        .invoke(configClass);
                blueprintsConfig.putDefaultConfiguration(configuration, directory);
            } catch (ClassNotFoundException e) {
                NeoLogger.warn(e, "Unable to find the configuration class {0}", configClassQualifiedName);
            } catch (NoSuchMethodException e) {
                NeoLogger.warn(e, "Unable to find configuration methods in class {0}", configClassName);
            } catch (InvocationTargetException | IllegalAccessException e) {
                NeoLogger.warn(e, "An error occurs during the execution of a configuration method");
            }
        } else {
            NeoLogger.warn("Unable to compute graph type name from {0}", graphType);
        }

        return configuration;
    }

    private static class Holder {

        private static final PersistenceBackendFactory INSTANCE = new BlueprintsPersistenceBackendFactory();
    }
}