org.texai.kb.persistence.DistributedRepositoryManager.java Source code

Java tutorial

Introduction

Here is the source code for org.texai.kb.persistence.DistributedRepositoryManager.java

Source

/*
 * DistributedRepositoryManager.java
 *
 * Created on May 18, 2009, 12:48:39 PM
 *
 * Description: Provides a distributed repository manager.
 *
 * Copyright (C) May 18, 2009 Stephen L. Reed.
 *
 * This program is free software; you can redistribute it and/or modify it under the terms
 * of the GNU General Public License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with this program;
 * if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
package org.texai.kb.persistence;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.xml.bind.DatatypeConverter;
import net.jcip.annotations.NotThreadSafe;
import net.jcip.annotations.ThreadSafe;
import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;
import org.apache.ws.jaxme.impl.DatatypeConverterImpl;
import org.openrdf.model.URI;
import org.openrdf.model.ValueFactory;
import org.openrdf.repository.Repository;
import org.openrdf.repository.RepositoryConnection;
import org.openrdf.repository.RepositoryException;
import org.openrdf.repository.http.HTTPRepository;
import org.openrdf.repository.sail.SailRepository;
import org.openrdf.sail.nativerdf.NativeStore;
import org.texai.kb.persistence.domainEntity.RepositoryContentDescription;
import org.texai.kb.persistence.domainEntity.RepositoryContentDescriptionItem;
import org.texai.kb.persistence.parser.ParseException;
import org.texai.kb.persistence.parser.RepositoryContentDescriptionParser;
import org.texai.util.FileSystemUtils;
import org.texai.util.StringUtils;
import org.texai.util.TexaiException;

/** Provides a repository manager.
 *
 * @author Stephen L. Reed
 */
@NotThreadSafe
public final class DistributedRepositoryManager {

    /** the log4j logger */
    private static final Logger LOGGER = Logger.getLogger(DistributedRepositoryManager.class);
    /** the indicator whether debug logging is enabled */
    private static final boolean IS_DEBUG_LOGGING_ENABLED = LOGGER.isDebugEnabled();
    /** the singleton distributed repository manager instance */
    private static DistributedRepositoryManager distributedRepositoryManager;
    /** the local repository dictionary, repository name --> local repository */
    private final Map<String, Repository> localRepositoryDictionary = new HashMap<>();
    /** the remote repository dictionary, repository name --> remote repository */
    private final Map<String, Repository> remoteRepositoryDictionary = new HashMap<>();
    /** the class name repository dictionary, class name --> repository name */
    private final Map<String, String> classNameRepositoryDictionary = new HashMap<>();
    /** the testing repository name */
    private String testRepositoryName = null;
    /** the repository content description repository */
    private Repository repositoryContentDescriptionRepository;
    /** the initialization lock */
    private static final Object DISTRIBUTED_REPOSITORY_MANAGER_LOCK = new Object();
    /** the not-initialized phase */
    private static final int NOT_INITIALIZED_PHASE = 0;
    /** the initialized from file stage */
    private static final int INITIALIZED_USING_FILE_PHASE = 1;
    /** the initialized from repository stage */
    private static final int INITIALIZED_USING_REPOSITORY_PHASE = 2;
    /** the initialization phase */
    private static int initializationPhase = NOT_INITIALIZED_PHASE;
    /** the repository content descriptions */
    private Set<RepositoryContentDescription> repositoryContentDescriptions;
    /** the repository path dictionary, repository name --> path to data directory */
    private static final Map<String, String> REPOSITORY_PATH_DICTIONARY = new HashMap<>();
    /** the Sesame server host URL, or null to use the local native store */
    private static String sesameServerAddress = null;
    /** the directory in which the production repositories are located */
    private static final String REPOSITORIES_DIRECTORY = System.getenv("REPOSITORIES");
    /** the directory in which the test repositories are located */
    private static final String TEST_REPOSITORIES_DIRECTORY = System.getenv("REPOSITORIES_TMPFS");

    /** Constructs a new DistributedRepositoryManager instance.  */
    public DistributedRepositoryManager() {
    }

    /** Copies the named repository from the production directory to the test directory.
     *
     * @param repositoryName the name of the repository to copy
     */
    public static void copyProductionRepositoryToTest(final String repositoryName) {
        //Preconditions
        assert StringUtils.isNonEmptyString(repositoryName) : "repositoryName must not be empty";
        assert StringUtils.isNonEmptyString(
                REPOSITORIES_DIRECTORY) : "REPOSITORIES must set to the production repositories directory";
        assert StringUtils.isNonEmptyString(
                TEST_REPOSITORIES_DIRECTORY) : "REPOSITORIES_TMPFS must set to the test repositories directory, e.g. /mnt/tmpfs/repositories";

        try {
            final String testRepositoryPath = TEST_REPOSITORIES_DIRECTORY + '/' + repositoryName;
            final File testRepositoryDirectory = new File(testRepositoryPath);
            try {
                if (testRepositoryDirectory.exists()) {
                    LOGGER.info("cleaning the test repository for " + repositoryName);
                    FileUtils.cleanDirectory(testRepositoryDirectory);
                }
            } catch (final IOException ex) {
                throw new TexaiException(ex);
            }

            final String repositoryPath = REPOSITORIES_DIRECTORY + '/' + repositoryName;
            final File repositoryDirectory = new File(repositoryPath);
            LOGGER.info("copying the " + repositoryName + " from production (" + repositoryDirectory.getPath()
                    + ") to test (" + testRepositoryDirectory.getPath() + ")");
            FileUtils.copyDirectory(repositoryDirectory, testRepositoryDirectory);
        } catch (IOException ex) {
            throw new TexaiException(ex);
        }
    }

    /** Gets the Sesame server host URL.
     *
     * @return the sesameServerAddress the Sesame server host URL, or null to use the local native store
     */
    public static String getSesameServerAddress() {
        return sesameServerAddress;
    }

    /** Sets the Sesame server host URL.
     *
     * @param _sesameServerAddress the Sesame server host URL, or null to use the local native store
     */
    public static void setSesameServerAddress(final String _sesameServerAddress) {
        sesameServerAddress = _sesameServerAddress;
    }

    /** Adds the given repository name and path entry to the repository path dictionary.
     *
     * @param repositoryName the repository name
     * @param repositoryPath the path to the repository's data directory
     */
    public static void addRepositoryPath(final String repositoryName, final String repositoryPath) {
        //Preconditions
        assert StringUtils.isNonEmptyString(repositoryName) : "repositoryName must not be empty";
        assert StringUtils.isNonEmptyString(repositoryPath) : "repositoryPath must not be empty";

        synchronized (REPOSITORY_PATH_DICTIONARY) {
            REPOSITORY_PATH_DICTIONARY.put(repositoryName, repositoryPath);
        }
    }

    /** Adds the given test repository name and its looked-up path entry to the repository path dictionary.
     *
     * @param repositoryName the repository name
     * @param isRepositoryDirectoryCleaned the indicator to clean the the directory containing the given named test repository files
     */
    public static void addTestRepositoryPath(final String repositoryName,
            final boolean isRepositoryDirectoryCleaned) {
        //Preconditions
        assert StringUtils.isNonEmptyString(repositoryName) : "repositoryName must not be empty";
        assert StringUtils.isNonEmptyString(
                TEST_REPOSITORIES_DIRECTORY) : "REPOSITORIES_TMPFS must set to the test repositories directory, e.g. /mnt/tmpfs/repositories";

        final String testRepositoryPath = TEST_REPOSITORIES_DIRECTORY + '/' + repositoryName;
        final File testRepositoryDirectory = new File(testRepositoryPath);
        if (isRepositoryDirectoryCleaned) {
            try {
                if (testRepositoryDirectory.exists()) {
                    FileUtils.cleanDirectory(testRepositoryDirectory);
                }
            } catch (final IOException ex) {
                throw new TexaiException(ex);
            }
        }
        assert testRepositoryDirectory != null : "testRepositoryDirectory must not be null";

        synchronized (REPOSITORY_PATH_DICTIONARY) {
            REPOSITORY_PATH_DICTIONARY.put(repositoryName, testRepositoryPath);
        }
    }

    /** Gets the singleton distributed repository manager instance, while performing two phase initialization.
     *
     * @return the singleton distributed repository manager instance
     */
    public static synchronized DistributedRepositoryManager getInstance() {
        if (initializationPhase == NOT_INITIALIZED_PHASE) {
            // because the RDF entity manager depends upon an initialized distributed repository manager, it cannot be
            // used to persist or load the repository content description objects
            LOGGER.info("initializing the distributed repository manager using file input");
            synchronized (DISTRIBUTED_REPOSITORY_MANAGER_LOCK) {
                initializationPhase = INITIALIZED_USING_FILE_PHASE;
                assert distributedRepositoryManager == null;
                distributedRepositoryManager = new DistributedRepositoryManager();
                distributedRepositoryManager.initializeUsingFile();
                deleteNamedRepository("RepositoryContentDescriptions");
                RDFEntityManager.setDistributedRepositoryManager(distributedRepositoryManager); // inject dependency

                // conveniently perform other one-time class initializations
                DatatypeConverter.setDatatypeConverter(new DatatypeConverterImpl());
            }
        } else if (initializationPhase == INITIALIZED_USING_FILE_PHASE) {
            // the distributed repository manager has been initialized from a file, so it can be used by the RDF entity
            // manager to persist the repository content descriptions into a repository
            LOGGER.info("initializing the distributed repository manager by persisting its entities");
            synchronized (DISTRIBUTED_REPOSITORY_MANAGER_LOCK) {
                initializationPhase = INITIALIZED_USING_REPOSITORY_PHASE;
                assert distributedRepositoryManager != null;
                distributedRepositoryManager.initializeUsingRepository();
            }
        }

        //Postconditions
        assert distributedRepositoryManager != null : "distributedRepositoryManager must not be null, initializationPhase: "
                + initializationPhase;

        return distributedRepositoryManager;
    }

    /** Clears the named repository.
     *
     * @param repositoryName the repository name
     */
    public static void clearNamedRepository(final String repositoryName) {
        //Preconditions
        assert StringUtils.isNonEmptyString(repositoryName) : "repositoryName must not be empty";

        final RepositoryConnection repositoryConnection = getInstance()
                .getRepositoryConnectionForRepositoryName(repositoryName);
        final String repositoryPath = repositoryConnection.getRepository().getDataDir().toString();
        if (repositoryPath.equals(System.getenv("REPOSITORIES") + "/OpenCyc")) {
            throw new TexaiException("attempting to clear the OpenCyc repository");
        }
        try {
            LOGGER.info("clearing the repository: " + repositoryPath);
            repositoryConnection.clear();
            repositoryConnection.close();
        } catch (final RepositoryException ex) {
            throw new TexaiException(ex);
        }
    }

    /** Deletes the named repository.
     *
     * @param repositoryName the repository name
     */
    public static void deleteNamedRepository(final String repositoryName) {
        //Preconditions
        assert StringUtils.isNonEmptyString(repositoryName) : "repositoryName must not be empty";
        assert StringUtils.isNonEmptyString(
                REPOSITORIES_DIRECTORY) : "REPOSITORIES must set to the production repositories directory";

        String repositoryPath;
        synchronized (REPOSITORY_PATH_DICTIONARY) {
            repositoryPath = REPOSITORY_PATH_DICTIONARY.get(repositoryName);
        }
        if (repositoryPath == null) {
            repositoryPath = REPOSITORIES_DIRECTORY + "/" + repositoryName;
        }
        final File dataDirectory = new File(repositoryPath);
        LOGGER.info("deleting Sesame2 repository in " + dataDirectory.toString());
        FileSystemUtils.deleteRecursively(dataDirectory);
    }

    /** Initializes the repository content descriptions without using the RDF entity manager to load or persist them. */
    private void initializeUsingFile() {
        //Preconditions
        assert StringUtils.isNonEmptyString(
                REPOSITORIES_DIRECTORY) : "REPOSITORIES must set to the production repositories directory";

        BufferedInputStream bufferedInputStream;
        File repositoryContentDescriptionsFile;
        repositoryContentDescriptionsFile = new File(REPOSITORIES_DIRECTORY + "/repository.rcd");
        assert repositoryContentDescriptionsFile.exists() : REPOSITORIES_DIRECTORY + "/repository.rcd not found";
        try {
            bufferedInputStream = new BufferedInputStream(new FileInputStream(repositoryContentDescriptionsFile));
        } catch (FileNotFoundException ex) {
            assert false;
            bufferedInputStream = null;
        }

        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("parsing the repository content descriptions file "
                    + repositoryContentDescriptionsFile.toString());
        }
        final RepositoryContentDescriptionParser repositoryContentDescriptionParser = new RepositoryContentDescriptionParser(
                bufferedInputStream);
        try {
            repositoryContentDescriptions = repositoryContentDescriptionParser.parseInput();
        } catch (final ParseException ex) {
            throw new TexaiException(ex);
        }
        final Iterator<RepositoryContentDescription> repositoryContentDescription_iter = repositoryContentDescriptions
                .iterator();
        while (repositoryContentDescription_iter.hasNext()) {
            final RepositoryContentDescription repositoryContentDescription = repositoryContentDescription_iter
                    .next();
            if (IS_DEBUG_LOGGING_ENABLED) {
                LOGGER.debug("processing repository content description for: " + repositoryContentDescription);
            }
            final String repositoryName = repositoryContentDescription.getRepositoryName();
            distributedRepositoryManager.addLocalRepository(
                    new LazyRepository(repositoryName, repositoryContentDescription.getIndices()));
            for (final RepositoryContentDescriptionItem repositoryContentDescriptionItem : repositoryContentDescription
                    .getRepositoryContentDescriptionItems()) {
                final URI classTerm = repositoryContentDescriptionItem.getClassTerm();
                if (classTerm != null) {
                    if (IS_DEBUG_LOGGING_ENABLED) {
                        LOGGER.debug("  " + classTerm.getLocalName() + " ==> " + repositoryName);
                    }
                    distributedRepositoryManager.classNameRepositoryDictionary.put(classTerm.getLocalName(),
                            repositoryName);
                }
            }
        }
    }

    /** Persists the repository content descriptions to their containing repository using the RDF entity manager, which in turn
     * depends upon having an initialized distributed repository manager.
     */
    private void initializeUsingRepository() {
        //Preconditions
        assert repositoryContentDescriptions != null : "repositoryContentDescriptions must not be null";

        final RDFEntityManager rdfEntityManager = new RDFEntityManager();
        for (final RepositoryContentDescription repositoryContentDescription : repositoryContentDescriptions) {
            LOGGER.info(repositoryContentDescription.getRepositoryName());
            rdfEntityManager.persist(repositoryContentDescription);
        }
        rdfEntityManager.close();
    }

    /** Shuts down the initialized repositories. */
    public static synchronized void shutDown() {
        if (distributedRepositoryManager != null) {
            for (final Repository repository : distributedRepositoryManager.localRepositoryDictionary.values()) {
                if (repository instanceof LazyRepository) {
                    final Repository repository1 = ((LazyRepository) repository).getRepository();
                    if (repository1 != null) {
                        LOGGER.info("shutting down lazy " + repository1.getDataDir());
                        final ShutDownRepository shutDownRepository = new ShutDownRepository(repository1);
                        final Thread thread = new Thread(shutDownRepository);
                        thread.start();
                    }
                } else {
                    LOGGER.info("shutting down " + repository.getDataDir());
                    final ShutDownRepository shutDownRepository = new ShutDownRepository(repository);
                    final Thread thread = new Thread(shutDownRepository);
                    thread.start();
                }
            }
        }
    }

    /** Shuts down a given repository. */
    private static class ShutDownRepository implements Runnable {

        /** the repository */
        private final Repository repository;

        /** Creates a new ShutDownRepository instance.
         *
         * @param repository the repository
         */
        ShutDownRepository(final Repository repository) {
            this.repository = repository;
        }

        /** Shuts down the repository. */
        @Override
        public void run() {
            try {
                repository.shutDown();
            } catch (final RepositoryException ex) {
                throw new TexaiException(ex);
            }
        }
    }

    /** Gets the local repository dictionary, name --> local repository.
     *
     * @return the local repository dictionary, name --> local repository
     */
    public synchronized Map<String, Repository> getLocalRepositoryDictionary() {
        return localRepositoryDictionary;
    }

    /** Adds a local repository.
     *
     * @param repository the local repository
     */
    public synchronized void addLocalRepository(final Repository repository) {
        //Preconditions
        assert repository != null : "repository must not be null";

        if (repository instanceof LazyRepository) {
            localRepositoryDictionary.put(((LazyRepository) repository).getRepositoryName(), repository);
        } else {
            localRepositoryDictionary.put(repository.getDataDir().getName(), repository);
        }
    }

    /** Gets the remote repository dictionary, name --> remote repository.
     *
     * @return the remote repository dictionary, name --> remote repository
     */
    public synchronized Map<String, Repository> getRemoteRepositoryDictionary() {
        return remoteRepositoryDictionary;
    }

    /** Returns a repository connection corresponding to the given repository name, which the caller must eventually close.
     *
     * @param repositoryName the given repository name.
     * @return a repository connection
     */
    public synchronized RepositoryConnection getRepositoryConnectionForRepositoryName(final String repositoryName) {
        //Preconditions
        assert repositoryName != null : "repositoryName must not be null";
        assert !repositoryName.isEmpty() : "repositoryName must not be empty";

        RepositoryConnection repositoryConnection = null;
        //TODO fix up for remote repositories
        try {
            final Repository repository = localRepositoryDictionary.get(repositoryName);
            if (repository == null) {
                throw new TexaiException(
                        "repository not found: " + repositoryName + "\n" + localRepositoryDictionary);
            }
            repositoryConnection = repository.getConnection();
        } catch (final RepositoryException ex) {
            throw new TexaiException(ex);
        }

        //Postconditons
        assert repositoryConnection != null : "repositoryConnection must not be null";

        return repositoryConnection;
    }

    /** Gets the testing repository name.
     *
     * @return the testing repository name
     */
    public String getTestRepositoryName() {
        return testRepositoryName;
    }

    /** Sets the testing repository name
     *
     * @param testRepositoryName the testing repository name
     */
    public void setTestRepositoryName(final String testRepositoryName) {
        this.testRepositoryName = testRepositoryName;
    }

    /** Gets the repository content description repository.
     *
     * @return the repository content description repository
     */
    public Repository getRepositoryContentDescriptionRepository() {
        return repositoryContentDescriptionRepository;
    }

    /** Sets the repository content description repository
     *
     * @param repositoryContentDescriptionRepository the repository content description repository
     */
    public void setRepositoryContentDescriptionRepository(final Repository repositoryContentDescriptionRepository) {
        //preconditions
        assert repositoryContentDescriptionRepository != null : "repositoryContentDescriptionRepository must not be null";

        this.repositoryContentDescriptionRepository = repositoryContentDescriptionRepository;
    }

    /** Returns the repository name corresponding to the given URI which identifies an RDF entity.
     *
     * @param uri the given URI which identifies an RDF entity
     * @return the repository name
     */
    public String getRepositoryNameForInstance(final URI uri) {
        //preconditions
        assert uri != null : "uri must not be null";

        if (testRepositoryName == null) {
            // typical case
            final String className = parseClassNameFromURI(uri);
            assert className != null : "cannot find repository name for instance: " + uri;
            return getRepositoryNameForClassName(className);
        } else {
            // testing - a test repository has been added to this distributed repository manager
            return testRepositoryName;
        }
    }

    /** Returns the repository name corresponding to the given RDF entity.
     *
     * @param rdfEntity the RDF entity
     * @return the repository name corresponding to the given RDF entity
     */
    public String getRepositoryNameForInstance(final RDFPersistent rdfEntity) {
        //preconditions
        assert rdfEntity != null : "rdfEntity must not be null";

        return getRepositoryNameForInstance(rdfEntity.getId());
    }

    /** Returns the repository name corresponding to the given RDF entity class.
     *
     * @param clazz the class
     * @return the repository name, or null if not found
     */
    public String getRepositoryNameForClass(final Class<?> clazz) {
        //preconditions
        assert clazz != null : "className clazz not be null";

        return getRepositoryNameForClassName(clazz.getName());
    }

    /** Returns the repository name corresponding to the given RDF entity class name.
     *
     * @param className the class name
     * @return the repository name, or null if not found
     */
    public String getRepositoryNameForClassName(final String className) {
        //preconditions
        assert className != null : "className must not be null";
        assert !className.isEmpty() : "className must not be empty";

        if (className.startsWith("org.texai")) {
            return classNameRepositoryDictionary.get(className);
        } else {
            // handle legacy WordNet IDs
            return classNameRepositoryDictionary.get("org.texai.wordnet.domain.entity." + className);
        }
    }

    /** Logs the class name repository dictionary. */
    public static void logClassNameRepositoryDictionary() {
        if (distributedRepositoryManager == null) {
            return;
        }
        final List<String> orderedClassNames = new ArrayList<>(
                distributedRepositoryManager.classNameRepositoryDictionary.keySet());
        Collections.sort(orderedClassNames);
        for (final String orderedClassName : orderedClassNames) {
            final String repositoryName = distributedRepositoryManager.classNameRepositoryDictionary
                    .get(orderedClassName);
            LOGGER.info(orderedClassName + " --> " + repositoryName);
        }
    }

    /** Returns the class name parsed from the given URI which identifies an RDF entity.
     *
     * @param uri the given URI which identifies an RDF entity
     * @return the class name parsed from the given URI if it has the format of an RDF entity id, otherwise returns null
     */
    public static String parseClassNameFromURI(final URI uri) {
        //preconditions
        assert uri != null : "uri must not be null";

        final String localName = uri.getLocalName();
        int index = -1;
        for (int i = localName.length() - 1; i >= 0; i--) {
            if (localName.charAt(i) == '_') {
                index = i;
                break;
            }
        }
        if (index == -1) {
            return null;
        } else {
            return localName.substring(0, index);
        }
    }

    /** Provides a repository for lazy initialization. */
    @ThreadSafe
    public static final class LazyRepository implements Repository {

        /** the repository name */
        private final String repositoryName;
        /** the indices */
        private final String indices;
        /** the lazily initialized repository */
        private Repository repository;

        /** Constructs a new LazyRepository instance.
         *
         * @param repositoryName the repository name
         * @param indices the indices
         */
        public LazyRepository(final String repositoryName, final String indices) {
            //Preconditions
            assert repositoryName != null : "repositoryName must not be null";
            assert !repositoryName.isEmpty() : "repositoryName must not be empty";
            assert indices != null : "indices must not be null";
            assert !indices.isEmpty() : "indices must not be empty";

            this.repositoryName = repositoryName;
            this.indices = indices;
        }

        /** Immediately initializes this repository. */
        private synchronized void immediatelyInitialize() {
            //Preconditions
            assert StringUtils.isNonEmptyString(
                    REPOSITORIES_DIRECTORY) : "REPOSITORIES must set to the production repositories directory";

            LOGGER.info("initializing: " + repositoryName);
            if (repository == null) {
                try {
                    if (sesameServerAddress == null) {
                        String repositoryPath = REPOSITORY_PATH_DICTIONARY.get(repositoryName);
                        if (repositoryPath == null) {
                            repositoryPath = REPOSITORIES_DIRECTORY + "/" + repositoryName;
                        }
                        final File dataDirectory = new File(repositoryPath);
                        LOGGER.info("accessing local Sesame2 repository in " + dataDirectory.toString());
                        repository = new SailRepository(new NativeStore(dataDirectory, indices));
                    } else {
                        LOGGER.info("accessing remote Sesame2 repository at " + sesameServerAddress + "/"
                                + repositoryName);
                        repository = new HTTPRepository(sesameServerAddress, repositoryName);
                        repository.setDataDir(new File(sesameServerAddress + "/" + repositoryName));
                        LOGGER.info("remote repository: " + repository);
                    }
                    repository.initialize();
                } catch (final RepositoryException ex) {
                    LOGGER.error("error while initializing: " + repositoryName);
                    throw new TexaiException(ex);
                }
            }
        }

        /** Sets the directory where data and logging for this repository is stored.
         *
         * @param dataDir the directory where data for this repository is stored
         */
        @Override
        public synchronized void setDataDir(final File dataDir) {
            throw new UnsupportedOperationException("Not supported.");
        }

        /** Gets the directory where data and logging for this repository is stored.
         *
         * @return the directory where data and logging for this repository is stored
         */
        @Override
        public synchronized File getDataDir() {
            if (repository == null) {
                immediatelyInitialize();
            }
            return repository.getDataDir();
        }

        /** Initializes this repository. A repository needs to be initialized before it can be used.
         *
         * @throws RepositoryException when a repository error occurs
         */
        @Override
        public synchronized void initialize() throws RepositoryException {
            if (repository == null) {
                immediatelyInitialize();
            }
            repository.initialize();
        }

        /** Shuts the repository down, releasing any resources that it keeps hold of.
         * Once shut down, the repository can no longer be used until it is re-initialized.
         *
         * @throws RepositoryException when a repository error occurs
         */
        @Override
        public synchronized void shutDown() throws RepositoryException {
            if (repository == null) {
                immediatelyInitialize();
            }
            repository.shutDown();
        }

        /** Checks whether this repository is writable, i.e. if the data contained in this repository can be changed.
         * The writability of the repository is determined by the writability of the Sail that this repository operates on.
         *
         * @return whether this repository is writable
         * @throws RepositoryException when a repository error occurs
         */
        @Override
        public synchronized boolean isWritable() throws RepositoryException {
            if (repository == null) {
                immediatelyInitialize();
            }
            return repository.isWritable();
        }

        /** Opens a connection to this repository that can be used for querying and updating the contents of the repository.
         * Created connections need to be closed to make sure that any resources they keep hold of are released.
         *
         * @return a repository connection
         * @throws RepositoryException when a repository error occurs
         */
        @Override
        public synchronized RepositoryConnection getConnection() throws RepositoryException {
            if (repository == null) {
                immediatelyInitialize();
            }
            return repository.getConnection();
        }

        /** Gets a ValueFactory for this Repository.
         *
         * @return a ValueFactory for this Repository
         */
        @Override
        public synchronized ValueFactory getValueFactory() {
            if (repository == null) {
                immediatelyInitialize();
            }
            return repository.getValueFactory();
        }

        /** Gets the repository name.
         *
         * @return the repositoryName
         */
        public synchronized String getRepositoryName() {
            return repositoryName;
        }

        /** Gets the indices.
         *
         * @return the indices
         */
        public synchronized String getIndices() {
            return indices;
        }

        /** Gets the lazily initialized repository.
         *
         * @return the repository
         */
        public synchronized Repository getRepository() {
            return repository;
        }

        @Override
        public boolean isInitialized() {
            return true;
        }
    }
}