/*
###############################################################################
# #
# Copyright (C) 2011-2016 OpenMEAP, Inc. #
# Credits to Jonathan Schang & Rob Thacher #
# #
# Released under the LGPLv3 #
# #
# OpenMEAP is free software: you can redistribute it and/or modify #
# it under the terms of the GNU Lesser General Public License as published #
# by the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# OpenMEAP is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU Lesser General Public License for more details. #
# #
# You should have received a copy of the GNU Lesser General Public License #
# along with OpenMEAP. If not, see <http://www.gnu.org/licenses/>. #
# #
###############################################################################
*/
package com.openmeap.model.event.notifier;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipFile;
import javax.persistence.PersistenceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.openmeap.cluster.ClusterNotificationException;
import com.openmeap.constants.UrlParamConstants;
import com.openmeap.digest.DigestException;
import com.openmeap.event.Event;
import com.openmeap.event.EventNotificationException;
import com.openmeap.event.MessagesEvent;
import com.openmeap.event.ProcessingEvent;
import com.openmeap.file.FileOperationManager;
import com.openmeap.model.ArchiveFileHelper;
import com.openmeap.model.ModelEntity;
import com.openmeap.model.ModelManager;
import com.openmeap.model.ModelServiceOperation;
import com.openmeap.model.dto.ApplicationArchive;
import com.openmeap.model.dto.GlobalSettings;
import com.openmeap.model.event.ModelEntityEventAction;
import com.openmeap.util.Utils;
import com.openmeap.util.ZipUtils;
public class ArchiveFileUploadNotifier extends AbstractModelServiceEventNotifier<ApplicationArchive> {
private Logger logger = LoggerFactory.getLogger(ArchiveFileUploadNotifier.class);
private ModelManager modelManager;
private FileOperationManager fileManager;
public void setModelManager(ModelManager modelManager) {
this.modelManager = modelManager;
}
public void setFileManager(FileOperationManager fileManager) {
this.fileManager = fileManager;
}
@Override
public Boolean notifiesFor(ModelServiceOperation operation,
ModelEntity payload) {
return ModelServiceOperation.SAVE_OR_UPDATE==operation && ApplicationArchive.class.isAssignableFrom(payload.getClass());
}
@Override
public <E extends Event<ApplicationArchive>> void onBeforeOperation(E event, List<ProcessingEvent> events) throws EventNotificationException {
ApplicationArchive archive = (ApplicationArchive)event.getPayload();
boolean newUpload = archive.getNewFileUploaded()!=null;
ApplicationArchive ret = archive;
if( newUpload && (ret=processApplicationArchiveFileUpload(archive,events))==null ) {
throw new PersistenceException("Zip archive failed to process!");
} else {
event.setPayload(ret);
}
}
@SuppressWarnings("unchecked")
private ApplicationArchive processApplicationArchiveFileUpload(ApplicationArchive archive, List<ProcessingEvent> events) {
GlobalSettings settings = modelManager.getGlobalSettings();
File tempFile = new File(archive.getNewFileUploaded());
Long size = tempFile.length();
String pathError = settings.validateTemporaryStoragePath();
if( pathError!=null ) {
logger.error("There is an issue with the global settings temporary storage path: "+settings.validateTemporaryStoragePath()+"\n {}",pathError);
events.add( new MessagesEvent("There is an issue with the global settings temporary storage path: "+settings.validateTemporaryStoragePath()+" - "+pathError) );
return null;
}
// determine the md5 hash of the uploaded file
// and rename the temp file by the hash
FileInputStream is = null;
File destinationFile = null;
try {
// if the archive is pre-existing and not the same,
// then determine if the web-view and zip should be deleted
// that is, they are not used by any other versions
if( archive.getId()!=null ) {
ArchiveFileHelper.maintainFileSystemCleanliness(modelManager,fileManager,archive,events);
}
String hashValue = null;
ApplicationArchive archiveExists = null;
try {
is = new FileInputStream(tempFile);
hashValue = Utils.hashInputStream("MD5", is);
archiveExists = modelManager.getModelService().findApplicationArchiveByHashAndAlgorithm(archive.getApplication(), hashValue, "MD5");
if(archiveExists!=null) {
if( !tempFile.delete() ) {
String mesg = String.format("Failed to delete temporary file %s",tempFile.getName());
logger.error(mesg);
events.add(new MessagesEvent(mesg));
}
return archiveExists;
} else {
// if an archive of the hash/alg doesn't exist,
// then we don't want to accidentally change an existing one.
archive = archive.clone();
archive.setId(null);
}
} catch(DigestException de) {
throw new PersistenceException(de);
} finally {
is.close();
}
archive.setHashAlgorithm("MD5");
archive.setHash(hashValue);
destinationFile = archive.getFile(settings.getTemporaryStoragePath());
// if the upload destination exists, then try to delete and overwrite
// even though they are theoretically the same.
if( destinationFile.exists() && !destinationFile.delete() ) {
String mesg = String.format("Failed to delete old file (theoretically the same anyways, so proceeding) %s",destinationFile.getName());
logger.error(mesg);
events.add(new MessagesEvent(mesg));
if( ! tempFile.delete() ) {
mesg = String.format("Failed to delete temporary file %s",tempFile.getName());
logger.error(mesg);
events.add(new MessagesEvent(mesg));
}
}
// if it didn't exist or it was successfully deleted,
// then rename the upload to our destination and unzip it
// into the web-view directory
else if( tempFile.renameTo(destinationFile) ) {
String mesg = String.format("Uploaded temporary file %s successfully renamed to %s",tempFile.getName(),destinationFile.getName());
logger.debug(mesg);
events.add(new MessagesEvent(mesg));
ArchiveFileHelper.unzipFile(modelManager,fileManager,archive,destinationFile,events);
} else {
String mesg = String.format("Failed to renamed file %s to %s",tempFile.getName(),destinationFile.getName());
logger.error(mesg);
events.add(new MessagesEvent(mesg));
return null;
}
} catch(IOException ioe) {
events.add(new MessagesEvent(ioe.getMessage()));
return null;
}
// determine the compressed and uncompressed size of the zip archive
try {
archive.setBytesLength(size.intValue());
ZipFile zipFile = null;
try {
zipFile = new ZipFile(destinationFile);
Integer uncompressedSize = ZipUtils.getUncompressedSize(zipFile).intValue();
archive.setBytesLengthUncompressed(new Long(uncompressedSize).intValue());
} finally {
if(zipFile!=null) {
zipFile.close();
}
}
} catch( IOException ioe ) {
logger.error("An exception occurred while calculating the uncompressed size of the archive: {}",ioe);
events.add(new MessagesEvent(String.format("An exception occurred while calculating the uncompressed size of the archive: %s",ioe.getMessage())));
return null;
}
archive.setUrl(ApplicationArchive.URL_TEMPLATE);
return archive;
}
}