uk.gov.nationalarchives.droid.signature.SignatureManagerImpl Maven / Gradle / Ivy
/**
* Copyright (c) 2016, The National Archives
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following
* conditions are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of the The National Archives nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package uk.gov.nationalarchives.droid.signature;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import uk.gov.nationalarchives.droid.core.interfaces.config.DroidGlobalConfig;
import uk.gov.nationalarchives.droid.core.interfaces.config.DroidGlobalProperty;
import uk.gov.nationalarchives.droid.core.interfaces.signature.ErrorCode;
import uk.gov.nationalarchives.droid.core.interfaces.signature.ProxySettings;
import uk.gov.nationalarchives.droid.core.interfaces.signature.SignatureFileException;
import uk.gov.nationalarchives.droid.core.interfaces.signature.SignatureFileInfo;
import uk.gov.nationalarchives.droid.core.interfaces.signature.SignatureManager;
import uk.gov.nationalarchives.droid.core.interfaces.signature.SignatureManagerException;
import uk.gov.nationalarchives.droid.core.interfaces.signature.SignatureServiceException;
import uk.gov.nationalarchives.droid.core.interfaces.signature.SignatureType;
import uk.gov.nationalarchives.droid.core.interfaces.signature.SignatureUpdateService;
/**
* @author rflitcroft
*
*/
public class SignatureManagerImpl implements SignatureManager {
private static final String INVALID_SIGNATURE_FILE = "Invalid signature file [%s]";
private static Map defaultVersionProperties =
new HashMap();
static {
defaultVersionProperties.put(SignatureType.BINARY, DroidGlobalProperty.DEFAULT_BINARY_SIG_FILE_VERSION);
defaultVersionProperties.put(SignatureType.CONTAINER, DroidGlobalProperty.DEFAULT_CONTAINER_SIG_FILE_VERSION);
defaultVersionProperties.put(SignatureType.TEXT, DroidGlobalProperty.DEFAULT_TEXT_SIG_FILE_VERSION);
}
private final Log log = LogFactory.getLog(getClass());
private DroidGlobalConfig config;
private Map signatureUpdateServices;
private ProxySettings proxySettings = new ProxySettings();
/**
* Initailisation post-construct.
*/
public void init() {
config.getProperties().addConfigurationListener(proxySettings);
Configuration configuration = config.getProperties();
proxySettings = new ProxySettings();
proxySettings.setEnabled(configuration.getBoolean(DroidGlobalProperty.UPDATE_USE_PROXY.getName()));
proxySettings.setProxyHost(configuration.getString(DroidGlobalProperty.UPDATE_PROXY_HOST.getName()));
proxySettings.setProxyPort(configuration.getInt(DroidGlobalProperty.UPDATE_PROXY_PORT.getName()));
proxySettings.setEnabled(configuration.getBoolean(DroidGlobalProperty.UPDATE_USE_PROXY.getName()));
config.getProperties().addConfigurationListener(proxySettings);
for (SignatureUpdateService subscriber : signatureUpdateServices.values()) {
subscriber.init(config);
proxySettings.addProxySubscriber(subscriber);
config.getProperties().addConfigurationListener(subscriber);
}
proxySettings.notifyProxySubscribers();
}
/**
* {@inheritDoc}
*/
@Override
public Map> getAvailableSignatureFiles() {
File binSigFileDir = config.getSignatureFileDir();
File containerSigFileDir = config.getContainerSignatureDir();
//File textSigFileDir = config.getTextSignatureFileDir();
FileFilter xmlExtensionFilter = new FileFilter() {
@Override
public boolean accept(File pathname) {
return pathname.isFile() && "xml".equals(FilenameUtils.getExtension(pathname.getName()));
}
};
Map> availableSigFiles =
new HashMap>();
SignatureInfoParser parser = new SignatureInfoParser();
final String errorMessagePattern = "Unreadable signature file [%s]";
SortedMap binSigFiles = new TreeMap();
for (File file : binSigFileDir.listFiles(xmlExtensionFilter)) {
String fileName = FilenameUtils.getBaseName(file.getName());
try {
binSigFiles.put(fileName, forBinarySigFile(file, parser));
} catch (SignatureFileException e) {
log.warn(String.format(errorMessagePattern, file));
}
}
SortedMap containerSigFiles = new TreeMap();
for (File file : containerSigFileDir.listFiles(xmlExtensionFilter)) {
String fileName = FilenameUtils.getBaseName(file.getName());
try {
containerSigFiles.put(fileName, forSimpleVersionedFile(file, SignatureType.CONTAINER));
} catch (SignatureFileException e) {
log.warn(String.format(errorMessagePattern, file));
}
}
/*
SortedMap textSigFiles = new TreeMap();
for (File file : textSigFileDir.listFiles(xmlExtensionFilter)) {
String fileName = FilenameUtils.getBaseName(file.getName());
try {
textSigFiles.put(fileName, forSimpleVersionedFile(file, SignatureType.TEXT));
} catch (SignatureFileException e) {
log.warn(String.format(errorMessagePattern, file));
}
}
*/
availableSigFiles.put(SignatureType.BINARY, binSigFiles);
availableSigFiles.put(SignatureType.CONTAINER, containerSigFiles);
//availableSigFiles.put(SignatureType.TEXT, textSigFiles);
return availableSigFiles;
}
private static SignatureFileInfo forBinarySigFile(File file, SignatureInfoParser parser)
throws SignatureFileException {
final SignatureFileInfo signatureFileInfo = parser.parse(file);
signatureFileInfo.setFile(file);
return signatureFileInfo;
}
private static SignatureFileInfo forSimpleVersionedFile(File file, SignatureType type)
throws SignatureFileException {
// parse the version from the filename
String filename = FilenameUtils.getBaseName(file.getName());
try {
int version = Integer.valueOf(StringUtils.substringAfterLast(filename, "-"));
final SignatureFileInfo signatureFileInfo = new SignatureFileInfo(version, false, type);
signatureFileInfo.setFile(file);
return signatureFileInfo;
} catch (NumberFormatException e) {
String message = String.format("Invalid signature filename [%s]", file.getName());
throw new SignatureFileException(message, e, ErrorCode.INVALID_SIGNATURE_FILE);
}
}
/**
* @param config the config to set
*/
public void setConfig(DroidGlobalConfig config) {
this.config = config;
}
private static final class SignatureInfoParser {
SignatureFileInfo parse(File sigFile) throws SignatureFileException {
SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
final SignatureFileVersionHandler handler = new SignatureFileVersionHandler();
try {
SAXParser saxParser = saxParserFactory.newSAXParser();
saxParser.parse(sigFile, handler);
throw new SignatureFileException(String.format(
INVALID_SIGNATURE_FILE, sigFile), ErrorCode.INVALID_SIGNATURE_FILE);
} catch (ValidSignatureFileException e) {
return e.getInfo();
} catch (SAXException e) {
throw new SignatureFileException(String.format(
INVALID_SIGNATURE_FILE, sigFile), e,
ErrorCode.INVALID_SIGNATURE_FILE);
} catch (ParserConfigurationException e) {
throw new RuntimeException(e.getMessage(), e);
} catch (IOException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
}
/**
* Handler for <FFSignatureFile> elements.
*
*/
private static final class SignatureFileVersionHandler extends DefaultHandler {
private static final String ROOT_ELEMENT = "FFSignatureFile";
@Override
public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException {
if (ROOT_ELEMENT.equals(qName)) {
int version = Integer.valueOf(attributes.getValue("Version"));
SignatureFileInfo info = new SignatureFileInfo(version, false, SignatureType.BINARY);
throw new ValidSignatureFileException(info);
}
throw new SAXException(
String.format("Invalid signature file - root element was not [%s]", ROOT_ELEMENT));
}
}
/**
* Exception thrown purely to stop SAX parsing when a valid signature file is found.
* @author rflitcroft
*
*/
private static final class ValidSignatureFileException extends RuntimeException {
private static final long serialVersionUID = 5955330716555328779L;
private final SignatureFileInfo info;
/**
* @param info the signature file info
*/
ValidSignatureFileException(SignatureFileInfo info) {
this.info = info;
}
/**
* @return the info
*/
public SignatureFileInfo getInfo() {
return info;
}
}
/**
* {@inheritDoc}
*/
@Override
public Map getLatestSignatureFiles() {
final Configuration properties = config.getProperties();
properties.setProperty(DroidGlobalProperty.LAST_UPDATE_CHECK.getName(), System.currentTimeMillis());
Map latestSigFiles = new HashMap();
final Map> availableSignatureFiles =
getAvailableSignatureFiles();
for (Map.Entry entry : signatureUpdateServices.entrySet()) {
SignatureType type = entry.getKey();
SignatureUpdateService updateService = entry.getValue();
final Map signaturesForType = availableSignatureFiles.get(type);
try {
int latestVersionForType = getLatestVersionForType(type, signaturesForType);
SignatureFileInfo latestUpdate = updateService.getLatestVersion(latestVersionForType);
if (latestUpdate != null
&& latestUpdate.getVersion() > 0
&& !signaturesForType.containsValue(latestUpdate)) {
latestSigFiles.put(type, latestUpdate);
}
} catch (SignatureServiceException e) {
latestSigFiles.put(type, new SignatureFileInfo(e));
}
}
return latestSigFiles;
}
private int getLatestVersionForType(SignatureType type, Map signatures) {
int result = 0;
for (String key : signatures.keySet()) {
SignatureFileInfo info = signatures.get(key);
if (info.getVersion() > result) {
result = info.getVersion();
}
}
return result;
}
/**
* @param signatureUpdateServices the signatureUpdateServices to set
*/
public void setSignatureUpdateServices(Map signatureUpdateServices) {
this.signatureUpdateServices = signatureUpdateServices;
}
/**
* {@inheritDoc}
*/
@Override
public SignatureFileInfo downloadLatest(SignatureType type) throws SignatureManagerException {
File sigFileDir;
switch (type) {
case BINARY:
sigFileDir = config.getSignatureFileDir();
break;
case CONTAINER:
sigFileDir = config.getContainerSignatureDir();
break;
case TEXT:
sigFileDir = config.getTextSignatureFileDir();
break;
default:
throw new IllegalArgumentException("Invalid signature type : " + type);
}
try {
SignatureFileInfo info = signatureUpdateServices.get(type).importSignatureFile(sigFileDir);
final boolean autoSetDefaultSigFile = config.getBooleanProperty(DroidGlobalProperty.UPDATE_AUTOSET_DEFAULT);
if (autoSetDefaultSigFile) {
PropertiesConfiguration props = config.getProperties();
props.setProperty(defaultVersionProperties.get(type).getName(),
FilenameUtils.getBaseName(info.getFile().getName()));
try {
config.getProperties().save();
} catch (ConfigurationException e) {
log.error(e);
throw new SignatureManagerException(e);
}
}
return info;
} catch (SignatureServiceException e) {
throw new SignatureManagerException(e);
}
}
/**
* {@inheritDoc}
* @throws SignatureFileException on failure to retrieve signatures
*/
@Override
public Map getDefaultSignatures() throws SignatureFileException {
Map defaultSignatures = new HashMap();
final Map>
availableSignatureFiles = getAvailableSignatureFiles();
final String errorMessagePattern =
"Default signature file %s could not be found. Please check your signature settings.";
for (Map.Entry> sigFileEntry
: availableSignatureFiles.entrySet()) {
SignatureType type = sigFileEntry.getKey();
Map sigs = sigFileEntry.getValue();
DroidGlobalProperty defaultSigFile = defaultVersionProperties.get(type);
String defaultSigFileKey = config.getProperties().getString(defaultSigFile.getName());
if (StringUtils.isNotBlank(defaultSigFileKey)) {
SignatureFileInfo sigFileInfo = sigs.get(defaultSigFileKey);
if (sigFileInfo == null) {
String errorMessage = String.format(errorMessagePattern, config.getProperties()
.getString(defaultVersionProperties.get(type).getName()));
throw new SignatureFileException(errorMessage, ErrorCode.FILE_NOT_FOUND);
}
defaultSignatures.put(type, sigFileInfo);
}
}
return defaultSignatures;
}
/**
* {@inheritDoc}
*/
@Override
public SignatureFileInfo install(SignatureType type, File signatureFile, boolean setDefault)
throws SignatureFileException {
SignatureInfoParser parser = new SignatureInfoParser();
SignatureFileInfo sigFileInfo = forBinarySigFile(signatureFile, parser);
try {
FileUtils.copyFileToDirectory(signatureFile, config.getSignatureFileDir(), true);
File newSignatureFile = new File(config.getSignatureFileDir(), signatureFile.getName());
sigFileInfo.setFile(newSignatureFile);
if (setDefault) {
config.getProperties().setProperty(defaultVersionProperties.get(type).getName(),
FilenameUtils.getBaseName(newSignatureFile.getName()));
}
return sigFileInfo;
} catch (IOException e) {
log.error(e);
throw new SignatureFileException(e.getMessage(), e, ErrorCode.FILE_NOT_FOUND);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy