001: /*
002: * Copyright Aduna (http://www.aduna-software.com/) (c) 2007.
003: *
004: * Licensed under the Aduna BSD-style license.
005: */
006: package org.openrdf.repository.manager;
007:
008: import java.io.IOException;
009: import java.util.ArrayList;
010: import java.util.Collection;
011: import java.util.HashMap;
012: import java.util.Iterator;
013: import java.util.Map;
014: import java.util.Set;
015:
016: import org.slf4j.Logger;
017: import org.slf4j.LoggerFactory;
018:
019: import org.openrdf.repository.Repository;
020: import org.openrdf.repository.RepositoryConnection;
021: import org.openrdf.repository.RepositoryException;
022: import org.openrdf.repository.config.RepositoryConfig;
023: import org.openrdf.repository.config.RepositoryConfigException;
024: import org.openrdf.repository.config.RepositoryConfigUtil;
025:
026: /**
027: * A manager for {@link Repository}s. Every <tt>RepositoryManager</tt> has
028: * one SYSTEM repository and zero or more "user repositories". The SYSTEM
029: * repository contains data that describes the configuration of the other
030: * repositories (their IDs, which implementations of the Repository API to use,
031: * access rights, etc.). The other repositories are instantiated based on this
032: * configuration data.
033: *
034: * @author Arjohn Kampman
035: */
036: public abstract class RepositoryManager {
037:
038: /*-----------*
039: * Constants *
040: *-----------*/
041:
042: protected final Logger logger = LoggerFactory.getLogger(this
043: .getClass());
044:
045: /*-----------*
046: * Variables *
047: *-----------*/
048:
049: private final Map<String, Repository> repositories;
050:
051: /*--------------*
052: * Constructors *
053: *--------------*/
054:
055: /**
056: * Creates a new RepositoryManager that operates on the specfified base
057: * directory.
058: *
059: * @param baseDir
060: * The base directory where data for repositories can be stored, among
061: * other things.
062: */
063: public RepositoryManager() {
064: this .repositories = new HashMap<String, Repository>();
065: }
066:
067: /*---------*
068: * Methods *
069: *---------*/
070:
071: /**
072: * Initializes the repository manager.
073: *
074: * @throws RepositoryException
075: * If the manager failed to initialize the SYSTEM repository.
076: */
077: public void initialize() throws RepositoryException {
078: Repository systemRepository = createSystemRepository();
079:
080: synchronized (repositories) {
081: repositories.put(SystemRepository.ID, systemRepository);
082: }
083: }
084:
085: protected abstract Repository createSystemRepository()
086: throws RepositoryException;
087:
088: /**
089: * Gets the SYSTEM repository.
090: */
091: public Repository getSystemRepository() {
092: synchronized (repositories) {
093: return repositories.get(SystemRepository.ID);
094: }
095: }
096:
097: public Set<String> getRepositoryIDs() throws RepositoryException {
098: return RepositoryConfigUtil
099: .getRepositoryIDs(getSystemRepository());
100: }
101:
102: public boolean hasRepositoryConfig(String repositoryID)
103: throws RepositoryException, RepositoryConfigException {
104: return RepositoryConfigUtil.hasRepositoryConfig(
105: getSystemRepository(), repositoryID);
106: }
107:
108: public RepositoryConfig getRepositoryConfig(String repositoryID)
109: throws RepositoryConfigException, RepositoryException {
110: return RepositoryConfigUtil.getRepositoryConfig(
111: getSystemRepository(), repositoryID);
112: }
113:
114: /**
115: * Adds or updates the configuration of a repository to the manager's system
116: * repository. The system repository may already contain a configuration for
117: * a repository with the same ID as specified by <tt>config</tt>, in which
118: * case all previous configuration data for that repository will be cleared
119: * before the new configuration is added.
120: *
121: * @param config
122: * The repository configuration that should be added to or updated in
123: * the system repository.
124: * @throws RepositoryException
125: * If the manager failed to update it's system repository.
126: * @throws RepositoryConfigException
127: * If the manager doesn't know how to update a configuration due to
128: * inconsistent configuration data in the system repository. For
129: * example, this happens when there are multiple existing
130: * configurations with the concerning ID.
131: */
132: public void addRepositoryConfig(RepositoryConfig config)
133: throws RepositoryException, RepositoryConfigException {
134: RepositoryConfigUtil.updateRepositoryConfigs(
135: getSystemRepository(), config);
136: }
137:
138: /**
139: * Removes the configuration for the specified repository from the manager's
140: * system repository if such a configuration is present. Returns
141: * <tt>true</tt> if the system repository actually contained the specified
142: * repository configuration.
143: *
144: * @param repositoryID
145: * The ID of the repository whose configuration needs to be removed.
146: * @throws RepositoryException
147: * If the manager failed to update it's system repository.
148: * @throws RepositoryConfigException
149: * If the manager doesn't know how to remove a configuration due to
150: * inconsistent configuration data in the system repository. For
151: * example, this happens when there are multiple existing
152: * configurations with the concerning ID.
153: */
154: public boolean removeRepositoryConfig(String repositoryID)
155: throws RepositoryException, RepositoryConfigException {
156: logger.info("Removing repository configuration for {}.",
157: repositoryID);
158: boolean isRemoved = false;
159:
160: synchronized (repositories) {
161: isRemoved = RepositoryConfigUtil.removeRepositoryConfigs(
162: getSystemRepository(), repositoryID);
163:
164: if (isRemoved) {
165: logger
166: .debug(
167: "Shutdown repository {} after removal of configuration.",
168: repositoryID);
169: Repository repository = repositories
170: .remove(repositoryID);
171:
172: if (repository != null) {
173: repository.shutDown();
174: }
175: }
176: }
177:
178: return isRemoved;
179: }
180:
181: /**
182: * Gets the repository that is known by the specified ID from this manager.
183: *
184: * @param id
185: * A repository ID.
186: * @return A Repository object, or <tt>null</tt> if no repository was known
187: * for the specified ID.
188: * @throws RepositoryConfigException
189: * If no repository could be created due to invalid or incomplete
190: * configuration data.
191: */
192: public Repository getRepository(String id)
193: throws RepositoryConfigException, RepositoryException {
194: synchronized (repositories) {
195: Repository result = repositories.get(id);
196:
197: if (result == null) {
198: // First call, create and initialize the repository.
199: result = createRepository(id);
200:
201: if (result != null) {
202: repositories.put(id, result);
203: }
204: }
205:
206: return result;
207: }
208: }
209:
210: /**
211: * Returns all inititalized repositories. This method returns fast as no lazy
212: * creation of repositories takes place.
213: *
214: * @return An unmodifiable collection containing the initialized
215: * repositories.
216: * @see #getAllRepositories()
217: */
218: public Collection<Repository> getInitializedRepositories() {
219: synchronized (repositories) {
220: return new ArrayList<Repository>(repositories.values());
221: }
222: }
223:
224: /**
225: * Returns all configured repositories. This may be an expensive operation as
226: * it initializes repositories that have not been initialized yet.
227: *
228: * @return The Set of all Repositories defined in the SystemRepository.
229: * @see #getInitializedRepositories()
230: */
231: public Collection<Repository> getAllRepositories()
232: throws RepositoryConfigException, RepositoryException {
233: Set<String> idSet = getRepositoryIDs();
234:
235: ArrayList<Repository> result = new ArrayList<Repository>(idSet
236: .size());
237:
238: for (String id : idSet) {
239: result.add(getRepository(id));
240: }
241:
242: return result;
243: }
244:
245: /**
246: * Creates and initializes the repository with the specified ID.
247: *
248: * @param id
249: * A repository ID.
250: * @return The created repository, or <tt>null</tt> if no such repository
251: * exists.
252: * @throws RepositoryConfigException
253: * If no repository could be created due to invalid or incomplete
254: * configuration data.
255: */
256: protected abstract Repository createRepository(String id)
257: throws RepositoryConfigException, RepositoryException;
258:
259: /**
260: * Gets the repository that is known by the specified ID from this manager.
261: *
262: * @param id
263: * A repository ID.
264: * @return A Repository object, or <tt>null</tt> if no repository was known
265: * for the specified ID.
266: * @throws RepositoryException
267: * When not able to retrieve existing configurations
268: */
269: public abstract RepositoryInfo getRepositoryInfo(String id)
270: throws RepositoryException;
271:
272: public Collection<RepositoryInfo> getAllRepositoryInfos()
273: throws RepositoryException {
274: return getAllRepositoryInfos(false);
275: }
276:
277: public Collection<RepositoryInfo> getAllUserRepositoryInfos()
278: throws RepositoryException {
279: return getAllRepositoryInfos(true);
280: }
281:
282: /**
283: *
284: * @param skipSystemRepo
285: * @throws RepositoryException
286: * When not able to retrieve existing configurations
287: */
288: public abstract Collection<RepositoryInfo> getAllRepositoryInfos(
289: boolean skipSystemRepo) throws RepositoryException;
290:
291: /**
292: * Shuts down all initialized user repositories.
293: *
294: * @see #shutDown()
295: */
296: public void refresh() {
297: logger.debug("Refreshing repository information in manager...");
298: synchronized (repositories) {
299: Iterator<Map.Entry<String, Repository>> iter = repositories
300: .entrySet().iterator();
301:
302: while (iter.hasNext()) {
303: Map.Entry<String, Repository> entry = iter.next();
304: String repositoryID = entry.getKey();
305: Repository repository = entry.getValue();
306:
307: if (!SystemRepository.ID.equals(repositoryID)) {
308: iter.remove();
309:
310: try {
311: repository.shutDown();
312: } catch (RepositoryException e) {
313: logger.error("Failed to shut down repository",
314: e);
315: }
316:
317: // FIXME: data dirs of removed but uninitialized repositories are
318: // not cleaned up
319: try {
320: RepositoryConnection con = getSystemRepository()
321: .getConnection();
322: try {
323: if (RepositoryConfigUtil.getContext(con,
324: repositoryID) == null) {
325: logger
326: .info(
327: "Cleaning up repository {}, its configuration has been removed",
328: repositoryID);
329:
330: cleanUpRepository(repositoryID);
331: }
332: } finally {
333: con.close();
334: }
335: } catch (RepositoryException e) {
336: logger
337: .error(
338: "Failed to process repository configuration changes",
339: e);
340: } catch (RepositoryConfigException e) {
341: logger
342: .warn(
343: "Unable to determine if configuration for {} is still present in the system repository",
344: repositoryID);
345: } catch (IOException e) {
346: logger
347: .warn(
348: "Unable to remove data dir for removed repository {} ",
349: repositoryID);
350: }
351: }
352: }
353: }
354: }
355:
356: /**
357: * Shuts down all initialized repositories, including the SYSTEM repository.
358: *
359: * @see #refresh()
360: */
361: public void shutDown() {
362: synchronized (repositories) {
363: for (Repository repository : repositories.values()) {
364: try {
365: repository.shutDown();
366: } catch (RepositoryException e) {
367: logger.error("Repository shut down failed", e);
368: }
369: }
370:
371: repositories.clear();
372: }
373: }
374:
375: /**
376: * Clean up a removed repository. Note that the configuration for this
377: * repository is no longer present in the system repository.
378: *
379: * @param repositoryID
380: * the ID of the repository to clean up
381: * @throws IOException
382: */
383: protected abstract void cleanUpRepository(String repositoryID)
384: throws IOException;
385: }
|