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;
}
}
}