/* * Copyright Aduna (http://www.aduna-software.com/) (c) 2007. * * Licensed under the Aduna BSD-style license. */ package org.openrdf.repository.manager; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.openrdf.repository.Repository; import org.openrdf.repository.RepositoryConnection; import org.openrdf.repository.RepositoryException; import org.openrdf.repository.config.RepositoryConfig; import org.openrdf.repository.config.RepositoryConfigException; import org.openrdf.repository.config.RepositoryConfigUtil; /** * A manager for {@link Repository}s. Every <tt>RepositoryManager</tt> has * one SYSTEM repository and zero or more "user repositories". The SYSTEM * repository contains data that describes the configuration of the other * repositories (their IDs, which implementations of the Repository API to use, * access rights, etc.). The other repositories are instantiated based on this * configuration data. * * @author Arjohn Kampman */ public abstract class RepositoryManager { /*-----------* * Constants * *-----------*/ protected final Logger logger = LoggerFactory.getLogger(this.getClass()); /*-----------* * Variables * *-----------*/ private final Map<String, Repository> repositories; /*--------------* * Constructors * *--------------*/ /** * Creates a new RepositoryManager that operates on the specfified base * directory. * * @param baseDir * The base directory where data for repositories can be stored, among * other things. */ public RepositoryManager() { this.repositories = new HashMap<String, Repository>(); } /*---------* * Methods * *---------*/ /** * Initializes the repository manager. * * @throws RepositoryException * If the manager failed to initialize the SYSTEM repository. */ public void initialize() throws RepositoryException { Repository systemRepository = createSystemRepository(); synchronized (repositories) { repositories.put(SystemRepository.ID, systemRepository); } } protected abstract Repository createSystemRepository() throws RepositoryException; /** * Gets the SYSTEM repository. */ public Repository getSystemRepository() { synchronized (repositories) { return repositories.get(SystemRepository.ID); } } public Set<String> getRepositoryIDs() throws RepositoryException { return RepositoryConfigUtil.getRepositoryIDs(getSystemRepository()); } public boolean hasRepositoryConfig(String repositoryID) throws RepositoryException, RepositoryConfigException { return RepositoryConfigUtil.hasRepositoryConfig(getSystemRepository(), repositoryID); } public RepositoryConfig getRepositoryConfig(String repositoryID) throws RepositoryConfigException, RepositoryException { return RepositoryConfigUtil.getRepositoryConfig(getSystemRepository(), repositoryID); } /** * Adds or updates the configuration of a repository to the manager's system * repository. The system repository may already contain a configuration for * a repository with the same ID as specified by <tt>config</tt>, in which * case all previous configuration data for that repository will be cleared * before the new configuration is added. * * @param config * The repository configuration that should be added to or updated in * the system repository. * @throws RepositoryException * If the manager failed to update it's system repository. * @throws RepositoryConfigException * If the manager doesn't know how to update a configuration due to * inconsistent configuration data in the system repository. For * example, this happens when there are multiple existing * configurations with the concerning ID. */ public void addRepositoryConfig(RepositoryConfig config) throws RepositoryException, RepositoryConfigException { RepositoryConfigUtil.updateRepositoryConfigs(getSystemRepository(), config); } /** * Removes the configuration for the specified repository from the manager's * system repository if such a configuration is present. Returns * <tt>true</tt> if the system repository actually contained the specified * repository configuration. * * @param repositoryID * The ID of the repository whose configuration needs to be removed. * @throws RepositoryException * If the manager failed to update it's system repository. * @throws RepositoryConfigException * If the manager doesn't know how to remove a configuration due to * inconsistent configuration data in the system repository. For * example, this happens when there are multiple existing * configurations with the concerning ID. */ public boolean removeRepositoryConfig(String repositoryID) throws RepositoryException, RepositoryConfigException { logger.info("Removing repository configuration for {}.", repositoryID); boolean isRemoved = false; synchronized (repositories) { isRemoved = RepositoryConfigUtil.removeRepositoryConfigs(getSystemRepository(), repositoryID); if (isRemoved) { logger.debug("Shutdown repository {} after removal of configuration.", repositoryID); Repository repository = repositories.remove(repositoryID); if (repository != null) { repository.shutDown(); } } } return isRemoved; } /** * Gets the repository that is known by the specified ID from this manager. * * @param id * A repository ID. * @return A Repository object, or <tt>null</tt> if no repository was known * for the specified ID. * @throws RepositoryConfigException * If no repository could be created due to invalid or incomplete * configuration data. */ public Repository getRepository(String id) throws RepositoryConfigException, RepositoryException { synchronized (repositories) { Repository result = repositories.get(id); if (result == null) { // First call, create and initialize the repository. result = createRepository(id); if (result != null) { repositories.put(id, result); } } return result; } } /** * Returns all inititalized repositories. This method returns fast as no lazy * creation of repositories takes place. * * @return An unmodifiable collection containing the initialized * repositories. * @see #getAllRepositories() */ public Collection<Repository> getInitializedRepositories() { synchronized (repositories) { return new ArrayList<Repository>(repositories.values()); } } /** * Returns all configured repositories. This may be an expensive operation as * it initializes repositories that have not been initialized yet. * * @return The Set of all Repositories defined in the SystemRepository. * @see #getInitializedRepositories() */ public Collection<Repository> getAllRepositories() throws RepositoryConfigException, RepositoryException { Set<String> idSet = getRepositoryIDs(); ArrayList<Repository> result = new ArrayList<Repository>(idSet.size()); for (String id : idSet) { result.add(getRepository(id)); } return result; } /** * Creates and initializes the repository with the specified ID. * * @param id * A repository ID. * @return The created repository, or <tt>null</tt> if no such repository * exists. * @throws RepositoryConfigException * If no repository could be created due to invalid or incomplete * configuration data. */ protected abstract Repository createRepository(String id) throws RepositoryConfigException, RepositoryException; /** * Gets the repository that is known by the specified ID from this manager. * * @param id * A repository ID. * @return A Repository object, or <tt>null</tt> if no repository was known * for the specified ID. * @throws RepositoryException * When not able to retrieve existing configurations */ public abstract RepositoryInfo getRepositoryInfo(String id) throws RepositoryException; public Collection<RepositoryInfo> getAllRepositoryInfos() throws RepositoryException { return getAllRepositoryInfos(false); } public Collection<RepositoryInfo> getAllUserRepositoryInfos() throws RepositoryException { return getAllRepositoryInfos(true); } /** * * @param skipSystemRepo * @throws RepositoryException * When not able to retrieve existing configurations */ public abstract Collection<RepositoryInfo> getAllRepositoryInfos(boolean skipSystemRepo) throws RepositoryException; /** * Shuts down all initialized user repositories. * * @see #shutDown() */ public void refresh() { logger.debug("Refreshing repository information in manager..."); synchronized (repositories) { Iterator<Map.Entry<String, Repository>> iter = repositories.entrySet().iterator(); while (iter.hasNext()) { Map.Entry<String, Repository> entry = iter.next(); String repositoryID = entry.getKey(); Repository repository = entry.getValue(); if (!SystemRepository.ID.equals(repositoryID)) { iter.remove(); try { repository.shutDown(); } catch (RepositoryException e) { logger.error("Failed to shut down repository", e); } // FIXME: data dirs of removed but uninitialized repositories are // not cleaned up try { RepositoryConnection con = getSystemRepository().getConnection(); try { if (RepositoryConfigUtil.getContext(con, repositoryID) == null) { logger.info("Cleaning up repository {}, its configuration has been removed", repositoryID); cleanUpRepository(repositoryID); } } finally { con.close(); } } catch (RepositoryException e) { logger.error("Failed to process repository configuration changes", e); } catch (RepositoryConfigException e) { logger.warn( "Unable to determine if configuration for {} is still present in the system repository", repositoryID); } catch (IOException e) { logger.warn("Unable to remove data dir for removed repository {} ", repositoryID); } } } } } /** * Shuts down all initialized repositories, including the SYSTEM repository. * * @see #refresh() */ public void shutDown() { synchronized (repositories) { for (Repository repository : repositories.values()) { try { repository.shutDown(); } catch (RepositoryException e) { logger.error("Repository shut down failed", e); } } repositories.clear(); } } /** * Clean up a removed repository. Note that the configuration for this * repository is no longer present in the system repository. * * @param repositoryID * the ID of the repository to clean up * @throws IOException */ protected abstract void cleanUpRepository(String repositoryID) throws IOException; }