/**
* Licensed to The Apereo Foundation under one or more contributor license
* agreements. See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*
*
* The Apereo Foundation licenses this file to you under the Educational
* Community License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of the License
* at:
*
* http://opensource.org/licenses/ecl2.txt
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*
*/
package org.opencastproject.workingfilerepository.impl;
import org.opencastproject.rest.RestConstants;
import org.opencastproject.serviceregistry.api.ServiceRegistry;
import org.opencastproject.systems.MatterhornConstants;
import org.opencastproject.util.Checksum;
import org.opencastproject.util.FileSupport;
import org.opencastproject.util.Log;
import org.opencastproject.util.NotFoundException;
import org.opencastproject.util.PathSupport;
import org.opencastproject.util.UrlSupport;
import org.opencastproject.util.data.Option;
import org.opencastproject.util.jmx.JmxUtil;
import org.opencastproject.workingfilerepository.api.PathMappable;
import org.opencastproject.workingfilerepository.api.WorkingFileRepository;
import org.opencastproject.workingfilerepository.jmx.WorkingFileRepositoryBean;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import javax.management.ObjectInstance;
/**
* A very simple (read: inadequate) implementation that stores all files under a root directory using the media package
* ID as a subdirectory and the media package element ID as the file name.
*/
public class WorkingFileRepositoryImpl implements WorkingFileRepository, PathMappable {
/** The logger */
private static final Logger logger = LoggerFactory.getLogger(WorkingFileRepositoryImpl.class);
private static final Log log = new Log(logger);
/** The extension we use for the md5 hash calculated from the file contents */
public static final String MD5_EXTENSION = ".md5";
/** The filename filter matching .md5 files */
private static final FilenameFilter MD5_FINAME_FILTER = new FilenameFilter() {
public boolean accept(File dir, String name) {
return name.endsWith(MD5_EXTENSION);
}
};
/** Working file repository JMX type */
private static final String JMX_WORKING_FILE_REPOSITORY_TYPE = "WorkingFileRepository";
/** The JMX working file repository bean */
private WorkingFileRepositoryBean workingFileRepositoryBean = new WorkingFileRepositoryBean(this);
/** The JMX bean object instance */
private ObjectInstance registeredMXBean;
/** The remote service manager */
protected ServiceRegistry remoteServiceManager;
/** The root directory for storing files */
protected String rootDirectory = null;
/** The Base URL for this server */
protected String serverUrl = null;
/** The URL for the services provided by the working file repository */
protected URI serviceUrl = null;
/**
* Activate the component
*/
public void activate(ComponentContext cc) throws IOException {
if (rootDirectory != null)
return; // If the root directory was set, respect that setting
// server url
serverUrl = cc.getBundleContext().getProperty(MatterhornConstants.SERVER_URL_PROPERTY);
if (StringUtils.isBlank(serverUrl))
throw new IllegalStateException("Server URL must be set");
// working file repository 'facade' configuration
String servicePath = (String) cc.getProperties().get(RestConstants.SERVICE_PATH_PROPERTY);
String canonicalFileRepositoryUrl = cc.getBundleContext().getProperty("org.opencastproject.file.repo.url");
if (StringUtils.isNotBlank(canonicalFileRepositoryUrl)) {
try {
this.serviceUrl = new URI(UrlSupport.concat(canonicalFileRepositoryUrl, servicePath));
} catch (URISyntaxException e) {
throw new IllegalStateException("Service URL must be a valid URI, but is " + canonicalFileRepositoryUrl, e);
}
} else {
try {
serviceUrl = new URI(UrlSupport.concat(serverUrl, servicePath));
} catch (URISyntaxException e) {
throw new IllegalStateException("Service URL can not be set to " + serverUrl + servicePath, e);
}
}
// root directory
rootDirectory = StringUtils.trimToNull(cc.getBundleContext().getProperty("org.opencastproject.file.repo.path"));
if (rootDirectory == null) {
String storageDir = cc.getBundleContext().getProperty("org.opencastproject.storage.dir");
if (storageDir == null) {
throw new IllegalStateException("Storage directory must be set");
}
rootDirectory = storageDir + File.separator + "files";
}
try {
createRootDirectory();
} catch (IOException e) {
logger.error("Unable to create the working file repository root directory at {}", rootDirectory);
throw e;
}
registeredMXBean = JmxUtil.registerMXBean(workingFileRepositoryBean, JMX_WORKING_FILE_REPOSITORY_TYPE);
logger.info(getDiskSpace());
}
/**
* Callback from OSGi on service deactivation.
*/
public void deactivate() {
JmxUtil.unregisterMXBean(registeredMXBean);
}
/**
* {@inheritDoc}
*
* @see org.opencastproject.workingfilerepository.api.WorkingFileRepository#delete(java.lang.String, java.lang.String)
*/
public boolean delete(String mediaPackageID, String mediaPackageElementID) throws IOException {
File f;
try {
f = getFile(mediaPackageID, mediaPackageElementID);
File parentDirectory = f.getParentFile();
logger.debug("Attempting to delete {}", parentDirectory.getAbsolutePath());
FileUtils.forceDelete(parentDirectory);
File parentsParentDirectory = parentDirectory.getParentFile();
if (parentsParentDirectory.isDirectory() && parentsParentDirectory.list().length == 0)
FileUtils.forceDelete(parentDirectory.getParentFile());
return true;
} catch (NotFoundException e) {
log.info("Unable to delete non existing media package element {}@{}", mediaPackageElementID, mediaPackageID);
return false;
}
}
/**
* {@inheritDoc}
*
* @see org.opencastproject.workingfilerepository.api.WorkingFileRepository#get(java.lang.String, java.lang.String)
*/
public InputStream get(String mediaPackageID, String mediaPackageElementID) throws NotFoundException, IOException {
File f = getFile(mediaPackageID, mediaPackageElementID);
logger.debug("Attempting to read file {}", f.getAbsolutePath());
return new FileInputStream(f);
}
/**
* {@inheritDoc}
*
* @see org.opencastproject.workingfilerepository.api.WorkingFileRepository#getCollectionURI(java.lang.String,
* java.lang.String)
*/
@Override
public URI getCollectionURI(String collectionID, String fileName) {
try {
return new URI(serviceUrl + COLLECTION_PATH_PREFIX + collectionID + "/" + PathSupport.toSafeName(fileName));
} catch (URISyntaxException e) {
throw new IllegalStateException("Unable to create valid uri from " + collectionID + " and " + fileName);
}
}
/**
* {@inheritDoc}
*
* @see org.opencastproject.workingfilerepository.api.WorkingFileRepository#getURI(java.lang.String, java.lang.String)
*/
public URI getURI(String mediaPackageID, String mediaPackageElementID) {
return getURI(mediaPackageID, mediaPackageElementID, null);
}
/**
* {@inheritDoc}
*
* @see org.opencastproject.workingfilerepository.api.WorkingFileRepository#getURI(java.lang.String, java.lang.String,
* java.lang.String)
*/
@Override
public URI getURI(String mediaPackageID, String mediaPackageElementID, String fileName) {
String uri = UrlSupport.concat(new String[]{getBaseUri().toString(), MEDIAPACKAGE_PATH_PREFIX, mediaPackageID,
mediaPackageElementID});
if (fileName == null) {
File existingDirectory = getElementDirectory(mediaPackageID, mediaPackageElementID);
if (existingDirectory.isDirectory()) {
File[] files = existingDirectory.listFiles();
boolean md5Exists = false;
for (File f : files) {
if (f.getName().endsWith(MD5_EXTENSION)) {
md5Exists = true;
} else {
fileName = f.getName();
}
}
if (md5Exists && fileName != null) {
uri = UrlSupport.concat(uri, PathSupport.toSafeName(fileName));
}
}
} else {
uri = UrlSupport.concat(uri, PathSupport.toSafeName(fileName));
}
try {
return new URI(uri);
} catch (URISyntaxException e) {
throw new IllegalArgumentException(e);
}
}
/**
* {@inheritDoc}
*
* @see org.opencastproject.workingfilerepository.api.WorkingFileRepository#put(java.lang.String, java.lang.String,
* java.lang.String, java.io.InputStream)
*/
public URI put(String mediaPackageID, String mediaPackageElementID, String filename, InputStream in)
throws IOException {
checkPathSafe(mediaPackageID);
checkPathSafe(mediaPackageElementID);
File f = null;
File dir = getElementDirectory(mediaPackageID, mediaPackageElementID);
if (dir.exists()) {
// clear the directory
File[] filesToDelete = dir.listFiles();
if (filesToDelete != null && filesToDelete.length > 0) {
for (File fileToDelete : filesToDelete) {
if (!fileToDelete.delete()) {
throw new IllegalStateException("Unable to delete file: " + fileToDelete.getAbsolutePath());
}
}
}
} else {
logger.debug("Attempting to create a new directory at {}", dir.getAbsolutePath());
FileUtils.forceMkdir(dir);
}
f = new File(dir, PathSupport.toSafeName(filename));
logger.debug("Attempting to write a file to {}", f.getAbsolutePath());
FileOutputStream out = null;
try {
if (!f.exists()) {
f.createNewFile();
} else {
logger.debug("Attempting to overwrite the file at {}", f.getAbsolutePath());
}
out = new FileOutputStream(f);
// Wrap the input stream and copy the input stream to the file
MessageDigest messageDigest = null;
DigestInputStream dis = null;
try {
messageDigest = MessageDigest.getInstance("MD5");
dis = new DigestInputStream(in, messageDigest);
IOUtils.copy(dis, out);
} catch (NoSuchAlgorithmException e1) {
logger.error("Unable to create md5 message digest");
}
// Store the hash
String md5 = Checksum.convertToHex(dis.getMessageDigest().digest());
File md5File = null;
try {
md5File = getMd5File(f);
FileUtils.writeStringToFile(md5File, md5);
} catch (IOException e) {
FileUtils.deleteQuietly(md5File);
throw e;
} finally {
IOUtils.closeQuietly(dis);
}
} catch (IOException e) {
FileUtils.deleteDirectory(dir);
throw e;
} finally {
IOUtils.closeQuietly(out);
IOUtils.closeQuietly(in);
}
return getURI(mediaPackageID, mediaPackageElementID, filename);
}
/**
* Creates a file containing the md5 hash for the contents of a source file.
*
* @param f
* the source file containing the data to hash
* @throws IOException
* if the hash cannot be created
*/
protected File createMd5(File f) throws IOException {
FileInputStream md5In = null;
File md5File = null;
try {
md5In = new FileInputStream(f);
String md5 = DigestUtils.md5Hex(md5In);
IOUtils.closeQuietly(md5In);
md5File = getMd5File(f);
FileUtils.writeStringToFile(md5File, md5);
return md5File;
} catch (IOException e) {
FileUtils.deleteQuietly(md5File);
throw e;
} finally {
IOUtils.closeQuietly(md5In);
}
}
/**
* Creates a file containing the md5 hash for the contents of a source file.
*
* @param is
* the input stream containing the data to hash
* @throws IOException
* if the hash cannot be created
*/
protected String createMd5(InputStream is) throws IOException {
File md5File = null;
try {
return DigestUtils.md5Hex(is);
} catch (IOException e) {
FileUtils.deleteQuietly(md5File);
throw e;
} finally {
IOUtils.closeQuietly(is);
}
}
/**
* Gets the file handle for an md5 associated with a content file. Calling this method and obtaining a File handle is
* not a guarantee that the md5 file exists.
*
* @param f
* The source file
* @return The md5 file
*/
private File getMd5File(File f) {
return new File(f.getParent(), f.getName() + MD5_EXTENSION);
}
/**
* Gets the file handle for a source file from its md5 file.
*
* @param md5File
* The md5 file
* @return The source file
*/
protected File getSourceFile(File md5File) {
return new File(md5File.getParent(), md5File.getName().substring(0, md5File.getName().length() - 4));
}
protected void checkPathSafe(String id) {
if (id == null)
throw new NullPointerException("IDs can not be null");
if (id.indexOf("..") > -1 || id.indexOf(File.separator) > -1) {
throw new IllegalArgumentException("Invalid media package, element ID, or file name");
}
}
/**
* Returns the file to the media package element.
*
* @param mediaPackageID
* the media package identifier
* @param mediaPackageElementID
* the media package element identifier
* @return the file or <code>null</code> if no such element exists
* @throws IllegalStateException
* if more than one matching elements were found
* @throws NotFoundException
* if the file cannot be found in the Working File Repository
*/
protected File getFile(String mediaPackageID, String mediaPackageElementID) throws IllegalStateException,
NotFoundException {
checkPathSafe(mediaPackageID);
checkPathSafe(mediaPackageElementID);
File directory = getElementDirectory(mediaPackageID, mediaPackageElementID);
File[] md5Files = directory.listFiles(MD5_FINAME_FILTER);
if (md5Files == null) {
logger.debug("Element directory {} does not exist", directory);
throw new NotFoundException("Element directory " + directory + " does not exist");
} else if (md5Files.length == 0) {
logger.debug("There are no complete files in the element directory {}", directory.getAbsolutePath());
throw new NotFoundException("There are no complete files in the element directory " + directory.getAbsolutePath());
} else if (md5Files.length == 1) {
File f = getSourceFile(md5Files[0]);
if (f.exists())
return f;
else
throw new NotFoundException("Unable to locate " + f + " in the working file repository");
} else {
logger.error("Integrity error: Element directory {} contains more than one element", mediaPackageID + "/"
+ mediaPackageElementID);
throw new IllegalStateException("Directory " + mediaPackageID + "/" + mediaPackageElementID
+ "does not contain exactly one element");
}
}
/**
* Returns the file from the given collection.
*
* @param collectionId
* the collection identifier
* @param fileName
* the file name
* @return the file
* @throws NotFoundException
* if either the collection or the file don't exist
*/
protected File getFileFromCollection(String collectionId, String fileName) throws NotFoundException,
IllegalArgumentException {
checkPathSafe(collectionId);
File directory = null;
try {
directory = getCollectionDirectory(collectionId, false);
if (directory == null)
throw new NotFoundException(fileName);
} catch (IOException e) {
// can be ignored, since we don't want the directory to be created, so it will never happen
}
File sourceFile = new File(directory, PathSupport.toSafeName(fileName));
File md5File = getMd5File(sourceFile);
if (!sourceFile.exists() || !md5File.exists()) {
throw new NotFoundException(fileName);
}
return sourceFile;
}
private File getElementDirectory(String mediaPackageID, String mediaPackageElementID) {
return new File(PathSupport.concat(new String[]{rootDirectory, MEDIAPACKAGE_PATH_PREFIX, mediaPackageID,
mediaPackageElementID}));
}
/**
* Returns a <code>File</code> reference to collection. If the collection does not exist, the method either returns
* <code>null</code> or creates it, depending on the parameter <code>create</code>.
*
* @param collectionId
* the collection identifier
* @param create
* whether to create a collection directory if it does not exist
* @return the collection directory or <code>null</code> if it does not exist and should not be created
* @throws IOException
* if creating a non-existing directory fails
*/
private File getCollectionDirectory(String collectionId, boolean create) throws IOException {
File collectionDir = new File(
PathSupport.concat(new String[]{rootDirectory, COLLECTION_PATH_PREFIX, collectionId}));
if (!collectionDir.exists()) {
if (!create)
return null;
try {
FileUtils.forceMkdir(collectionDir);
logger.debug("Created collection directory " + collectionId);
} catch (IOException e) {
// We check again to see if it already exists because this collection dir may live on a shared disk.
// Synchronizing does not help because the other instance is not in the same JVM.
if (!collectionDir.exists()) {
throw new IllegalStateException("Can not create collection directory" + collectionDir);
}
}
}
return collectionDir;
}
void createRootDirectory() throws IOException {
File f = new File(rootDirectory);
if (!f.exists())
FileUtils.forceMkdir(f);
}
/**
* {@inheritDoc}
*
* @see org.opencastproject.workingfilerepository.api.WorkingFileRepository#getCollectionSize(java.lang.String)
*/
@Override
public long getCollectionSize(String id) throws NotFoundException {
File collectionDir = null;
try {
collectionDir = getCollectionDirectory(id, false);
if (collectionDir == null || !collectionDir.canRead())
throw new NotFoundException("Can not find collection " + id);
} catch (IOException e) {
// can be ignored, since we don't want the directory to be created, so it will never happen
}
File[] files = collectionDir.listFiles(MD5_FINAME_FILTER);
if (files == null)
throw new IllegalArgumentException("Collection " + id + " is not a directory");
return files.length;
}
/**
* {@inheritDoc}
*
* @see org.opencastproject.workingfilerepository.api.WorkingFileRepository#getFromCollection(java.lang.String,
* java.lang.String)
*/
@Override
public InputStream getFromCollection(String collectionId, String fileName) throws NotFoundException, IOException {
File f = getFileFromCollection(collectionId, fileName);
if (f == null || !f.isFile()) {
throw new NotFoundException("Unable to locate " + f + " in the working file repository");
}
logger.debug("Attempting to read file {}", f.getAbsolutePath());
return new FileInputStream(f);
}
/**
* {@inheritDoc}
*
* @throws IOException
* if the hash can't be created
* @see org.opencastproject.workingfilerepository.api.WorkingFileRepository#putInCollection(java.lang.String,
* java.lang.String, java.io.InputStream)
*/
@Override
public URI putInCollection(String collectionId, String fileName, InputStream in) throws IOException {
checkPathSafe(collectionId);
checkPathSafe(fileName);
File f = new File(PathSupport.concat(new String[]{rootDirectory, COLLECTION_PATH_PREFIX, collectionId,
PathSupport.toSafeName(fileName)}));
logger.debug("Attempting to write a file to {}", f.getAbsolutePath());
FileOutputStream out = null;
try {
if (!f.exists()) {
logger.debug("Attempting to create a new file at {}", f.getAbsolutePath());
File collectionDirectory = getCollectionDirectory(collectionId, true);
if (!collectionDirectory.exists()) {
logger.debug("Attempting to create a new directory at {}", collectionDirectory.getAbsolutePath());
FileUtils.forceMkdir(collectionDirectory);
}
f.createNewFile();
} else {
logger.debug("Attempting to overwrite the file at {}", f.getAbsolutePath());
}
out = new FileOutputStream(f);
// Wrap the input stream and copy the input stream to the file
MessageDigest messageDigest = null;
DigestInputStream dis = null;
try {
messageDigest = MessageDigest.getInstance("MD5");
dis = new DigestInputStream(in, messageDigest);
IOUtils.copy(dis, out);
} catch (NoSuchAlgorithmException e1) {
logger.error("Unable to create md5 message digest");
}
// Store the hash
String md5 = Checksum.convertToHex(dis.getMessageDigest().digest());
File md5File = null;
try {
md5File = getMd5File(f);
FileUtils.writeStringToFile(md5File, md5);
} catch (IOException e) {
FileUtils.deleteQuietly(md5File);
throw e;
} finally {
IOUtils.closeQuietly(dis);
}
} catch (IOException e) {
FileUtils.deleteQuietly(f);
throw e;
} finally {
IOUtils.closeQuietly(out);
IOUtils.closeQuietly(in);
}
return getCollectionURI(collectionId, fileName);
}
/**
* {@inheritDoc}
*
* @see org.opencastproject.workingfilerepository.api.WorkingFileRepository#copyTo(java.lang.String, java.lang.String,
* java.lang.String, java.lang.String, java.lang.String)
*/
@Override
public URI copyTo(String fromCollection, String fromFileName, String toMediaPackage, String toMediaPackageElement,
String toFileName) throws NotFoundException, IOException {
File source = getFileFromCollection(fromCollection, fromFileName);
if (source == null)
throw new IllegalArgumentException("Source file " + fromCollection + "/" + fromFileName + " does not exist");
File destDir = getElementDirectory(toMediaPackage, toMediaPackageElement);
if (!destDir.exists()) {
// we needed to create the directory, but couldn't
try {
FileUtils.forceMkdir(destDir);
} catch (IOException e) {
throw new IllegalStateException("could not create mediapackage/element directory '" + destDir.getAbsolutePath()
+ "' : " + e);
}
}
File destFile;
try {
destFile = new File(destDir, PathSupport.toSafeName(toFileName));
FileSupport.link(source, destFile);
createMd5(destFile);
} catch (Exception e) {
FileUtils.deleteDirectory(destDir);
}
return getURI(toMediaPackage, toMediaPackageElement, toFileName);
}
/**
* {@inheritDoc}
*
* @see org.opencastproject.workingfilerepository.api.WorkingFileRepository#moveTo(java.lang.String, java.lang.String,
* java.lang.String, java.lang.String, java.lang.String)
*/
@Override
public URI moveTo(String fromCollection, String fromFileName, String toMediaPackage, String toMediaPackageElement,
String toFileName) throws NotFoundException, IOException {
File source = getFileFromCollection(fromCollection, fromFileName);
File sourceMd5 = getMd5File(source);
File destDir = getElementDirectory(toMediaPackage, toMediaPackageElement);
logger.debug("Moving {} from {} to {}/{}", new String[]{fromFileName, fromCollection, toMediaPackage,
toMediaPackageElement});
if (!destDir.exists()) {
// we needed to create the directory, but couldn't
try {
FileUtils.forceMkdir(destDir);
} catch (IOException e) {
throw new IllegalStateException("could not create mediapackage/element directory '" + destDir.getAbsolutePath()
+ "' : " + e);
}
}
File dest = null;
try {
dest = getFile(toMediaPackage, toMediaPackageElement);
logger.debug("Removing existing file from target location at {}", dest);
delete(toMediaPackage, toMediaPackageElement);
} catch (NotFoundException e) {
dest = new File(getElementDirectory(toMediaPackage, toMediaPackageElement), PathSupport.toSafeName(toFileName));
}
try {
FileUtils.moveFile(source, dest);
FileUtils.moveFile(sourceMd5, getMd5File(dest));
} catch (IOException e) {
FileUtils.deleteDirectory(destDir);
throw new IllegalStateException("unable to copy file" + e);
}
return getURI(toMediaPackage, toMediaPackageElement, dest.getName());
}
/**
* {@inheritDoc}
*
* @see org.opencastproject.workingfilerepository.api.WorkingFileRepository#deleteFromCollection(java.lang.String,
* java.lang.String)
*/
@Override
public boolean deleteFromCollection(String collectionId, String fileName) throws IOException {
File f = null;
try {
f = getFileFromCollection(collectionId, fileName);
} catch (NotFoundException e) {
logger.trace("File {}/{} does not exist", collectionId, fileName);
return false;
}
File md5File = getMd5File(f);
if (!f.isFile())
throw new IllegalStateException(f + " is not a regular file");
if (!md5File.isFile())
throw new IllegalStateException(md5File + " is not a regular file");
if (!md5File.delete())
throw new IOException("MD5 hash " + md5File + " cannot be deleted");
if (!f.delete())
throw new IOException(f + " cannot be deleted");
File parentDirectory = f.getParentFile();
if (parentDirectory.isDirectory() && parentDirectory.list().length == 0) {
logger.debug("Attempting to delete empty collection directory {}", parentDirectory.getAbsolutePath());
try {
FileUtils.forceDelete(parentDirectory);
} catch (IOException e) {
logger.warn("Unable to delete empty collection directory {}", parentDirectory.getAbsolutePath());
return false;
}
}
return true;
}
/**
* {@inheritDoc}
*
* @see org.opencastproject.workingfilerepository.api.WorkingFileRepository#getCollectionContents(java.lang.String)
*/
@Override
public URI[] getCollectionContents(String collectionId) throws NotFoundException {
File collectionDir = null;
try {
collectionDir = getCollectionDirectory(collectionId, false);
if (collectionDir == null)
throw new NotFoundException(collectionId);
} catch (IOException e) {
// We are not asking for the collection to be created, so this exception is never thrown
}
File[] files = collectionDir.listFiles(MD5_FINAME_FILTER);
URI[] uris = new URI[files.length];
for (int i = 0; i < files.length; i++) {
try {
uris[i] = new URI(serviceUrl + COLLECTION_PATH_PREFIX + collectionId + "/"
+ PathSupport.toSafeName(getSourceFile(files[i]).getName()));
} catch (URISyntaxException e) {
throw new IllegalStateException("Invalid URI for " + files[i]);
}
}
return uris;
}
/**
* Returns the md5 hash value for the given mediapackage element.
*
* @throws NotFoundException
* if the media package element does not exist
*/
String getMediaPackageElementDigest(String mediaPackageID, String mediaPackageElementID) throws IOException,
IllegalStateException, NotFoundException {
File f = getFile(mediaPackageID, mediaPackageElementID);
if (f == null)
throw new NotFoundException(mediaPackageID + "/" + mediaPackageElementID);
return getFileDigest(f);
}
/**
* Returns the md5 hash value for the given collection element.
*
* @throws NotFoundException
* if the collection element does not exist
*/
String getCollectionElementDigest(String collectionId, String fileName) throws IOException, NotFoundException {
return getFileDigest(getFileFromCollection(collectionId, fileName));
}
/**
* Returns the md5 of a file
*
* @param file
* the source file
* @return the md5 hash
*/
private String getFileDigest(File file) throws IOException {
if (file == null)
throw new IllegalArgumentException("File must not be null");
if (!file.exists() || !file.isFile())
throw new IllegalArgumentException("File " + file.getAbsolutePath() + " can not be read");
// Check if there is a precalculated md5 hash
File md5HashFile = getMd5File(file);
if (file.exists()) {
logger.trace("Reading precalculated hash for {} from {}", file, md5HashFile.getName());
return FileUtils.readFileToString(md5HashFile, "utf-8");
}
// Calculate the md5 hash
InputStream in = null;
String md5 = null;
try {
in = new FileInputStream(file);
md5 = DigestUtils.md5Hex(in);
} finally {
IOUtils.closeQuietly(in);
}
// Write the md5 hash to disk for later reference
try {
FileUtils.writeStringToFile(md5HashFile, md5, "utf-8");
} catch (IOException e) {
logger.warn("Error storing cached md5 checksum at {}", md5HashFile);
throw e;
}
return md5;
}
/**
* {@inheritDoc}
*
* @see org.opencastproject.workingfilerepository.api.WorkingFileRepository#getTotalSpace()
*/
public Option<Long> getTotalSpace() {
File f = new File(rootDirectory);
return Option.some(f.getTotalSpace());
}
/**
* {@inheritDoc}
*
* @see org.opencastproject.workingfilerepository.api.WorkingFileRepository#getUsableSpace()
*/
public Option<Long> getUsableSpace() {
File f = new File(rootDirectory);
return Option.some(f.getUsableSpace());
}
/**
* {@inheritDoc}
*
* @see org.opencastproject.workingfilerepository.api.WorkingFileRepository#getUsedSpace()
*/
@Override
public Option<Long> getUsedSpace() {
return Option.some(FileUtils.sizeOfDirectory(new File(rootDirectory)));
}
/**
* {@inheritDoc}
*
* @see org.opencastproject.workingfilerepository.api.WorkingFileRepository#getDiskSpace()
*/
public String getDiskSpace() {
int usable = Math.round(getUsableSpace().get() / 1024 / 1024 / 1024);
int total = Math.round(getTotalSpace().get() / 1024 / 1024 / 1024);
long percent = Math.round(100.0 * getUsableSpace().get() / (1 + getTotalSpace().get()));
return "Usable space " + usable + " Gb out of " + total + " Gb (" + percent + "%)";
}
/**
* {@inheritDoc}
*
* @see org.opencastproject.workingfilerepository.api.PathMappable#getPathPrefix()
*/
@Override
public String getPathPrefix() {
return rootDirectory;
}
/**
* {@inheritDoc}
*
* @see org.opencastproject.workingfilerepository.api.PathMappable#getUrlPrefix()
*/
@Override
public String getUrlPrefix() {
return serviceUrl.toString();
}
/**
* {@inheritDoc}
*
* @see org.opencastproject.workingfilerepository.api.WorkingFileRepository#getBaseUri()
*/
@Override
public URI getBaseUri() {
return serviceUrl;
}
/**
* Sets the remote service manager.
*
* @param remoteServiceManager
*/
public void setRemoteServiceManager(ServiceRegistry remoteServiceManager) {
this.remoteServiceManager = remoteServiceManager;
}
}