/*
* Copyright (C) 2006-2016 DLR, Germany
*
* All rights reserved
*
* http://www.rcenvironment.de/
*/
package de.rcenvironment.core.datamanagement.internal;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;
import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import de.rcenvironment.core.authorization.AuthorizationException;
import de.rcenvironment.core.communication.common.CommunicationException;
import de.rcenvironment.core.communication.common.ResolvableNodeId;
import de.rcenvironment.core.datamanagement.DataManagementService;
import de.rcenvironment.core.datamanagement.DataReferenceService;
import de.rcenvironment.core.datamanagement.FileDataService;
import de.rcenvironment.core.datamanagement.commons.DataReference;
import de.rcenvironment.core.datamanagement.commons.MetaDataSet;
import de.rcenvironment.core.utils.common.CrossPlatformFilenameUtils;
import de.rcenvironment.core.utils.common.StringUtils;
import de.rcenvironment.core.utils.common.TempFileServiceAccess;
/**
* Default implementation of {@link DataManagementService}.
*
* @author Robert Mischke
* @author Doreen Seider
*/
public class DataManagementServiceImpl implements DataManagementService {
private static final String STRING_FILENAME_NOT_VALID =
"Filename/path '%S' contains characters that are not valid for all operating systems; "
+ "it might lead to problems when accessing the file on another operating system";
private static final String ARCHIVE_TAR_GZ = "archive.tar.gz";
private static final int BUFFER = 1024;
private static final String REFERENCE_NOT_FOUND_MESSAGE = "No such data entry (id='%s').";
private static final String TAR_GZ_PATH_SEPARATOR = "/";
private static final Log LOGGER = LogFactory.getLog(DataManagementServiceImpl.class);
private FileDataService fileDataService;
private DataReferenceService dataReferenceService;
@Override
public String createReferenceFromLocalFile(File file, MetaDataSet additionalMetaData,
ResolvableNodeId nodeId) throws IOException, AuthorizationException, InterruptedException, CommunicationException {
if (!CrossPlatformFilenameUtils.isFilenameValid(file.getName())) {
LOGGER.warn(StringUtils.format(STRING_FILENAME_NOT_VALID, file.getName()));
}
return createReferenceFromStream(new FileInputStream(file), additionalMetaData, nodeId);
}
private String createReferenceFromStream(InputStream inputStream, MetaDataSet additionalMetaData,
ResolvableNodeId nodeId) throws IOException, AuthorizationException, InterruptedException, CommunicationException {
if (additionalMetaData == null) {
additionalMetaData = new MetaDataSet();
}
try {
DataReference dataRef = fileDataService.newReferenceFromStream(inputStream, additionalMetaData, nodeId);
return dataRef.getDataReferenceKey().toString();
} finally {
// FIXME: replace with try-with-resources statement after release 7.0.0
IOUtils.closeQuietly(inputStream);
}
}
@Override
public String createReferenceFromLocalDirectory(File dir, MetaDataSet additionalMetaData, ResolvableNodeId nodeId)
throws IOException, AuthorizationException, InterruptedException, CommunicationException {
File archive = TempFileServiceAccess.getInstance().createTempFileWithFixedFilename(ARCHIVE_TAR_GZ);
createTarGz(dir, archive);
try (FileInputStream fileInputStream = new FileInputStream(archive)) {
String reference = createReferenceFromStream(fileInputStream, additionalMetaData, nodeId);
TempFileServiceAccess.getInstance().disposeManagedTempDirOrFile(archive);
return reference;
}
}
// set visibility from private to protected for test purposes
protected void createTarGz(File dir, File archive) throws IOException {
try (FileOutputStream fileOutStream = new FileOutputStream(archive);
BufferedOutputStream bufferedOutStream = new BufferedOutputStream(fileOutStream);
GzipCompressorOutputStream gzipOutStream = new GzipCompressorOutputStream(bufferedOutStream);
TarArchiveOutputStream tarOutStream = new TarArchiveOutputStream(gzipOutStream)) {
addFileToTarGz(tarOutStream, dir.getAbsolutePath(), "");
}
}
private void addFileToTarGz(TarArchiveOutputStream tOutStream, String path, String base) throws IOException {
File file = new File(path);
if (!CrossPlatformFilenameUtils.isPathValid(file.getAbsolutePath())) {
LOGGER.warn(StringUtils.format(STRING_FILENAME_NOT_VALID, file.getAbsolutePath()));
}
String entryName = base + file.getName();
TarArchiveEntry tarEntry = new TarArchiveEntry(file, entryName);
tOutStream.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU);
tOutStream.putArchiveEntry(tarEntry);
if (file.isFile()) {
try (InputStream inputStream = new FileInputStream(file)) {
IOUtils.copy(inputStream, tOutStream);
}
tOutStream.closeArchiveEntry();
} else {
tOutStream.closeArchiveEntry();
File[] children = file.listFiles();
if (children != null) {
for (File child : children) {
if (!CrossPlatformFilenameUtils.isFilenameValid(child.getName())) {
LOGGER.warn(StringUtils.format(STRING_FILENAME_NOT_VALID, child.getName()));
}
addFileToTarGz(tOutStream, child.getAbsolutePath(), entryName + TAR_GZ_PATH_SEPARATOR);
}
}
}
}
@Override
public String createReferenceFromString(String object, MetaDataSet additionalMetaData, // CheckStyle
ResolvableNodeId nodeId) throws IOException, AuthorizationException, InterruptedException, CommunicationException {
if (additionalMetaData == null) {
additionalMetaData = new MetaDataSet();
}
InputStream inputStream = IOUtils.toInputStream(object);
try {
DataReference dataRef = fileDataService.newReferenceFromStream(inputStream, additionalMetaData, nodeId);
return dataRef.getDataReferenceKey().toString();
} finally {
// FIXME: replace with try-with-resources statement after release 7.0.0
IOUtils.closeQuietly(inputStream);
}
}
@Override
public void copyReferenceToLocalFile(String reference, File targetFile, // CheckStyle
ResolvableNodeId nodeId) throws IOException, CommunicationException {
DataReference dataRef;
if (nodeId == null) {
dataRef = dataReferenceService.getReference(reference);
} else {
dataRef = dataReferenceService.getReference(reference, nodeId);
}
if (dataRef == null) {
throw new FileNotFoundException(StringUtils.format(REFERENCE_NOT_FOUND_MESSAGE, reference));
}
InputStream dataMgmtStream = fileDataService.getStreamFromDataReference(dataRef);
try {
FileUtils.copyInputStreamToFile(dataMgmtStream, targetFile);
} finally {
// FIXME: replace with try-with-resources statement after release 7.0.0
IOUtils.closeQuietly(dataMgmtStream);
}
}
@Override
public void copyReferenceToLocalFile(String reference, File targetFile, // CheckStyle
Collection<? extends ResolvableNodeId> platforms) throws IOException, AuthorizationException, CommunicationException {
DataReference dataRef;
if ((platforms == null) || (platforms.size() == 0)) {
dataRef = dataReferenceService.getReference(reference);
} else {
dataRef = dataReferenceService.getReference(reference, platforms);
}
if (dataRef == null) {
throw new FileNotFoundException(StringUtils.format(REFERENCE_NOT_FOUND_MESSAGE, reference));
}
InputStream dataMgmtStream = fileDataService.getStreamFromDataReference(dataRef);
try {
FileUtils.copyInputStreamToFile(dataMgmtStream, targetFile);
} finally {
// FIXME: replace with try-with-resources statement after release 7.0.0
IOUtils.closeQuietly(dataMgmtStream);
}
}
@Override
public void copyReferenceToLocalDirectory(String reference, File targetDir, ResolvableNodeId node) throws IOException,
CommunicationException {
File archive = TempFileServiceAccess.getInstance().createTempFileWithFixedFilename(ARCHIVE_TAR_GZ);
copyReferenceToLocalFile(reference, archive, node);
createDirectoryFromTarGz(archive, targetDir);
TempFileServiceAccess.getInstance().disposeManagedTempDirOrFile(archive);
}
// set visibility to from private to protected for test purposes
protected void createDirectoryFromTarGz(File archive, File targetDir) throws FileNotFoundException, IOException {
targetDir.mkdirs();
try (FileInputStream fileInStream = new FileInputStream(archive);
BufferedInputStream bufferedInStream = new BufferedInputStream(fileInStream);
GzipCompressorInputStream gzipInStream = new GzipCompressorInputStream(bufferedInStream);
TarArchiveInputStream tarInStream = new TarArchiveInputStream(gzipInStream)) {
createFileOrDirForTarEntry(tarInStream, targetDir);
}
}
private void createFileOrDirForTarEntry(TarArchiveInputStream tarInStream, File targetDir) throws IOException {
TarArchiveEntry tarEntry;
while ((tarEntry = tarInStream.getNextTarEntry()) != null) {
File destPath = new File(targetDir, tarEntry.getName());
if (tarEntry.isDirectory()) {
destPath.mkdirs();
} else {
destPath.createNewFile();
byte[] btoRead = new byte[BUFFER];
try (BufferedOutputStream bout = new BufferedOutputStream(new FileOutputStream(destPath))) {
int len = 0;
final int minusOne = -1;
while ((len = tarInStream.read(btoRead)) != minusOne) {
bout.write(btoRead, 0, len);
}
}
}
}
}
@Override
public String retrieveStringFromReference(String reference, ResolvableNodeId nodeId) throws IOException,
AuthorizationException, CommunicationException {
// TODO extract reference to stream resolution into separate method?
DataReference dataRef;
if (nodeId == null) {
dataRef = dataReferenceService.getReference(reference);
} else {
dataRef = dataReferenceService.getReference(reference, nodeId);
}
if (dataRef == null) {
throw new FileNotFoundException(StringUtils.format(REFERENCE_NOT_FOUND_MESSAGE, reference));
}
InputStream dataMgmtStream = fileDataService.getStreamFromDataReference(dataRef);
try {
return IOUtils.toString(dataMgmtStream);
} finally {
// FIXME: replace with try-with-resources statement after release 7.0.0
IOUtils.closeQuietly(dataMgmtStream);
}
}
/**
* OSGi-DS setter.
*
* @param fileDataService The fileDataService to set.
*/
protected void bindFileDataService(FileDataService newValue) {
this.fileDataService = newValue;
}
/**
* OSGi-DS setter.
*
* @param dataReferenceService The dataReferenceService to set.
*/
protected void bindDataReferenceService(DataReferenceService newValue) {
this.dataReferenceService = newValue;
}
}