package nl.uvt.commons.oai; import nl.uvt.commons.lang.InvalidSettingException; import java.io.*; import java.net.*; import java.util.*; import org.apache.log4j.*; import org.joda.time.*; /** * DAO that uses properties files for configuration and status. * *

Simple solution, not to be used for serious production systems. * Since the (status) file is simply overwritten, there is a chance * that status information can get lost due to some IO error.

*/ public class FilesAdminDAO implements IAdminDAO { private File configurationDirectory; private File statusDirectory; private UUID uuid; // Fake connection handle private static Logger logger = Logger.getLogger(FilesAdminDAO.class); public FilesAdminDAO() { this.uuid = UUID.randomUUID(); } @Override public void setProperty(String key, String value) throws InvalidSettingException { if (key == null) { throw new InvalidSettingException("Key is Null"); } else if (key.equals("configurationDirectory")) { this.configurationDirectory = new File(value); if (! this.configurationDirectory.exists()) { logger.fatal("Configuration directory '"+this.configurationDirectory.getAbsolutePath()+"' does not exist"); } else if (! this.configurationDirectory.isDirectory()) { logger.fatal("Configuration directory '"+this.configurationDirectory.getAbsolutePath()+"' is not a directory"); } } else if (key.equals("statusDirectory")) { this.statusDirectory = new File(value); if (! this.statusDirectory.exists()) { logger.fatal("Status directory '"+this.statusDirectory.getAbsolutePath()+"' does not exist"); } else if (! this.statusDirectory.isDirectory()) { logger.fatal("Status directory '"+this.statusDirectory.getAbsolutePath()+"' is not a directory"); } } else { throw new InvalidSettingException("Unknown setting '"+key+"'"); } } @Override public void init() { } @Override public void setupInfrastructure() { } @Override public void loadDriver() { } @Override public boolean supportsTransactions() { return false; } @Override public boolean guardsConsistency() { return false; } @Override public UUID getConnectionHandle() { return this.uuid; } @Override public void closeConnection(UUID handle) { } @Override public void commit(UUID handle) { } @Override public void rollback(UUID handle) { } @Override public void putRepository(UUID handle, Repository repository, boolean overwrite) throws OAIException { try { Properties properties = new Properties(); properties.setProperty("repository.base-url", repository.getBaseURL().toExternalForm()); File file = this.getRepositoryFile(repository.getId()); if (file.exists() && overwrite == false) throw new OAIException("Trying to overwrite existing repository, id='"+repository.getId()+"'"); OutputStream os = new FileOutputStream(file); properties.store(os, "Updated by "+this.getClass().getName()+" at "+new Date()); os.close(); } catch (IOException e) { logger.error("IOException occurred while storing configuration for repository with id='"+repository.getId()+"'. Msg="+e.getMessage()); } } @Override public List getRepositoryIds(UUID handle) { return this.getIds("repository"); } @Override public Repository getRepository(UUID handle, String id) { Repository repository = null; try { Properties properties = new Properties(); InputStream is = new FileInputStream(this.getRepositoryFile(id)); properties.load(is); is.close(); String repositoryBaseURL = properties.getProperty("repository.base-url"); if (repositoryBaseURL != null) { repository = new Repository(id, new URL(repositoryBaseURL)); } } catch (IOException e) { logger.error("IOException occurred while loading configuration for repository with id='"+id+"'. Msg="+e.getMessage()); } return repository; } @Override public void removeRepository(UUID connectoon, String repositoryId) { File file = this.getRepositoryFile(repositoryId); file.delete(); } private File getRepositoryFile(String repositoryId) { return new File(this.configurationDirectory, repositoryId+".repository"); } @Override public void putFeed(UUID handle, Feed feed, boolean overwrite) throws OAIException { this.saveFeedStatus(feed); try { Properties properties = new Properties(); properties.setProperty("repository.id", feed.getRepository().getId()); properties.setProperty("feed.metadata-prefix", feed.getMetadataPrefix()); if (feed.getSet() != null) properties.setProperty("feed.set", feed.getSet()); File file = this.getFeedFile(feed.getId()); if (file.exists() && overwrite == false) throw new OAIException("Trying to overwrite existing feed, id='"+feed.getId()+"'"); OutputStream os = new FileOutputStream(file); properties.store(os, "Updated by "+this.getClass().getName()+" at "+new Date()); os.close(); } catch (IOException e) { logger.error("IOException occurred while storing configuration for feed with id='"+feed.getId()+"'. Msg="+e.getMessage()); } } @Override public List getFeedIds(UUID handle, String repositoryId) { List ids = new ArrayList(); for (String id : this.getIds("feed")) { Feed feed = this.getFeed(handle, id); if (feed.getRepository().getId().equals(id)) ids.add(id); } return ids; } @Override public Feed getFeed(UUID handle, String id) { Feed feed = null; try { Properties properties = new Properties(); InputStream is = new FileInputStream(this.getFeedFile(id)); properties.load(is); is.close(); feed = new Feed(id); String repositoryId = properties.getProperty("repository.id"); if (repositoryId != null) { Repository repository = this.getRepository(handle, repositoryId); feed.setRepository(repository); } String metadataPrefix = properties.getProperty("feed.metadata-prefix"); if (metadataPrefix != null) { feed.setMetadataPrefix(metadataPrefix); } String set = properties.getProperty("feed.set"); if (set != null) { feed.setSet(set); } this.loadFeedStatus(feed); } catch (IOException e) { logger.error("IOException occurred while loading configuration for feed with id='"+id+"'. Msg="+e.getMessage()); } return feed; } @Override public void removeFeed(UUID connectoon, String feedId) { File file = this.getFeedFile(feedId); file.delete(); } private File getFeedFile(String feedId) { return new File(this.configurationDirectory, feedId+".feed"); } private void loadFeedStatus(Feed feed) { String timestamp = null; try { Properties properties = new Properties(); InputStream is = new FileInputStream(new File(this.statusDirectory, feed.getId()+".feed-status")); properties.load(is); is.close(); /* * From: */ timestamp = properties.getProperty("feed.from"); if (timestamp != null) { if (timestamp.length() == 0) { feed.setFrom(null); } else { if (timestamp.length() == 10) timestamp += "T00:00:00Z"; feed.setFrom(Feed.dateTimeFormat.parseDateTime(timestamp).toDate()); } } else { feed.setFrom(null); } /* * Until: */ timestamp = properties.getProperty("feed.until"); if (timestamp != null) { if (timestamp.length() == 0) { feed.setUntil(null); } else { if (timestamp.length() == 10) timestamp += "T00:00:00Z"; feed.setUntil(Feed.dateTimeFormat.parseDateTime(timestamp).toDate()); } } else { feed.setUntil(null); } /* * Resumption token + expirationDate: */ String resumptionToken = properties.getProperty("feed.resumption-token"); if (resumptionToken != null) { if (resumptionToken.length() != 0) feed.setResumptionToken(resumptionToken); else feed.setResumptionToken(null); } else { feed.setResumptionToken(null); } timestamp = properties.getProperty("feed.resumption-token.expiration-date"); if (timestamp != null) { if (timestamp.length() == 0) { feed.setResumptionTokenExpirationDate(null); } else { if (timestamp.length() == 10) timestamp += "T00:00:00Z"; feed.setResumptionTokenExpirationDate(Feed.dateTimeFormat.parseDateTime(timestamp).toDate()); } } else { feed.setResumptionTokenExpirationDate(null); } } catch (IOException e) { logger.error("IOException occurred while loading status for feed with id='"+feed.getId()+"'. Msg="+e.getMessage()); } } private void saveFeedStatus(Feed feed) { Properties properties = new Properties(); if (feed.getFrom() != null) { properties.setProperty("feed.from", Feed.dateTimeFormat.print(new DateTime(feed.getFrom()))); } if (feed.getUntil() != null) { properties.setProperty("feed.until", Feed.dateTimeFormat.print(new DateTime(feed.getUntil()))); } if (feed.getResumptionToken() != null) { properties.setProperty("feed.resumption-token", feed.getResumptionToken()); } if (feed.getResumptionTokenExpirationDate() != null) { properties.setProperty( "feed.resumption-token.expiration-date", Feed.dateTimeFormat.print(new DateTime(feed.getResumptionTokenExpirationDate()))); } try { OutputStream os = new FileOutputStream(new File(this.statusDirectory, feed.getId()+".feed-status")); properties.store(os, "Written by feed '"+feed.getId()+"' at "+new Date()); } catch (SecurityException e) { logger.error("SecurityException occurred while saving status for feed with id='"+feed.getId()+"'. Msg="+e.getMessage()); } catch (FileNotFoundException e) { logger.error("FileNotFoundException occurred while saving status for feed with id='"+feed.getId()+"'. Msg="+e.getMessage()); } catch (IOException e) { logger.error("IOException occurred while saving status for feed with id='"+feed.getId()+"'. Msg="+e.getMessage()); } } /** * Returns names of files in configuration directory with certain extension. * *

Extension is case sensitive.

*

If configuration directory = /var/lib/dodoco, extension = "feed" and * configuration directory contains two files with this extension, foo.feed * and bar.feed, the method returns [ "bar", "foo" ].

* * @param extension filter on this extension (case sensitive) * @return sorted list of Ids */ private List getIds(String extension) { List ids = new ArrayList(); for (File file : this.configurationDirectory.listFiles(new ExtensionFileFilter(extension))) { // ExtensionFilter class below String name = file.getName(); ids.add(name.substring(0, name.length()-extension.length()-1)); } java.util.Collections.sort(ids); return ids; } private class ExtensionFileFilter implements FileFilter { private String extension; public ExtensionFileFilter(String extension) { this.extension = extension; } public boolean accept(File file) { if (file.getName().endsWith("."+this.extension)) return true; else return false; } } }