Java tutorial
/* ############################################################################### # # # Copyright (C) 2011-2012 OpenMEAP, Inc. # # Credits to Jonathan Schang & Robert 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.admin.web.backing; import static com.openmeap.util.ParameterMapUtils.firstValue; import static com.openmeap.util.ParameterMapUtils.notEmpty; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.zip.ZipFile; import javax.persistence.PersistenceException; import org.apache.commons.fileupload.FileItem; import org.apache.commons.io.FileUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.exception.ExceptionUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.openmeap.Authorizer; import com.openmeap.admin.web.events.AddSubNavAnchorEvent; import com.openmeap.constants.FormConstants; import com.openmeap.event.MessagesEvent; import com.openmeap.event.ProcessingEvent; import com.openmeap.event.ProcessingTargets; import com.openmeap.model.InvalidPropertiesException; import com.openmeap.model.ModelManager; import com.openmeap.model.ModelServiceOperation; import com.openmeap.model.dto.Application; import com.openmeap.model.dto.ApplicationArchive; import com.openmeap.model.dto.ApplicationVersion; import com.openmeap.model.dto.Deployment; import com.openmeap.model.dto.GlobalSettings; import com.openmeap.model.event.ModelEntityEvent; import com.openmeap.model.event.notifier.ArchiveFileUploadNotifier; import com.openmeap.protocol.dto.HashAlgorithm; import com.openmeap.util.ParameterMapUtils; import com.openmeap.util.ServletUtils; import com.openmeap.util.Utils; import com.openmeap.util.ZipUtils; import com.openmeap.web.AbstractTemplatedSectionBacking; import com.openmeap.web.GenericProcessingEvent; import com.openmeap.web.ProcessingContext; import com.openmeap.web.ProcessingUtils; import com.openmeap.web.html.Anchor; import com.openmeap.web.html.Option; // TODO: there are way to many things going on in this class public class AddModifyApplicationVersionBacking extends AbstractTemplatedSectionBacking { private Logger logger = LoggerFactory.getLogger(AddModifyApplicationVersionBacking.class); private static String PROCESS_TARGET = ProcessingTargets.ADDMODIFY_APPVER; private ModelManager modelManager = null; private ArchiveFileUploadNotifier archiveUploadNotifier = null; public AddModifyApplicationVersionBacking() { setProcessingTargetIds(Arrays.asList(new String[] { PROCESS_TARGET })); } /** * With the first of the bean name matching "addModifyApp", there are * three ways to access this: * - request has applicationId and processTarget - modifying an application * - request has applicationId only - pulling up an application to modify * - request has processTarget only - submitting a brand new application * * See the WEB-INF/ftl/form-application-addmodify.ftl for input/output parameters. * * @param context Not referenced at all, may be null * @param templateVariables Variables output to for the view * @param parameterMap Parameters passed in to drive processing * @return on errors, returns an array of error processingevents * @see TemplatedSectionBacking::process() */ public Collection<ProcessingEvent> process(ProcessingContext context, Map<Object, Object> templateVariables, Map<Object, Object> parameterMap) { List<ProcessingEvent> events = new ArrayList<ProcessingEvent>(); Application app = null; ApplicationVersion version = null; // make sure we're configured to accept uploads, warn otherwise validateStorageConfiguration(templateVariables, events); // we must have an application in order to add a version if (!notEmpty(FormConstants.APP_ID, parameterMap)) { return ProcessingUtils.newList(new GenericProcessingEvent<String>(ProcessingTargets.MESSAGES, "An application must be specified in order to add a version")); } Long appId = Long.valueOf(firstValue(FormConstants.APP_ID, parameterMap)); app = modelManager.getModelService().findByPrimaryKey(Application.class, appId); if (app == null) { return ProcessingUtils.newList(new GenericProcessingEvent<String>(ProcessingTargets.MESSAGES, "The application with id " + appId + " could not be found.")); } events.add(new AddSubNavAnchorEvent(new Anchor("?bean=addModifyAppPage&applicationId=" + app.getId(), "View/Modify Application", "View/Modify Application"))); events.add(new AddSubNavAnchorEvent(new Anchor("?bean=appVersionListingsPage&applicationId=" + app.getId(), "Version Listings", "Version Listings"))); events.add(new AddSubNavAnchorEvent(new Anchor("?bean=deploymentListingsPage&applicationId=" + app.getId(), "Deployment History", "Deployment History"))); // at this point, we're committed to form setup at least templateVariables.put(FormConstants.PROCESS_TARGET, PROCESS_TARGET); version = obtainExistingApplicationVersionFromParameters(app, appId, events, parameterMap); if (version == null) { version = new ApplicationVersion(); } // determine if the user is allowed to modify application versions Boolean willProcess = canUserModifyOrCreate(app, version); if (!willProcess) { events.add(new MessagesEvent("Current user does not have permissions to make changes here.")); } if (!version.getActiveFlag()) { events.add(new MessagesEvent("This version is not currently active.")); willProcess = false; } templateVariables.put("willProcess", willProcess); if (notEmpty(FormConstants.PROCESS_TARGET, parameterMap) && PROCESS_TARGET.compareTo(firstValue(FormConstants.PROCESS_TARGET, parameterMap)) == 0 && willProcess) { // TODO: check to see if the user can delete versions if (ParameterMapUtils.notEmpty(FormConstants.DELETE, parameterMap) && ParameterMapUtils.notEmpty("deleteConfirm", parameterMap)) { if (ParameterMapUtils.firstValue("deleteConfirm", parameterMap) .equals(FormConstants.APPVER_DELETE_CONFIRM_TEXT)) { try { modelManager.begin(); modelManager.delete(version, events); modelManager.commit(events); } catch (Exception e) { modelManager.rollback(); String msg = String.format("Unable to delete the version - %s", ExceptionUtils.getRootCauseMessage(e)); logger.error(msg, e); events.add(new MessagesEvent(msg)); } } else { events.add(new MessagesEvent( "You must confirm your desire to delete by typing in the delete confirmation message.")); } } else { processApplicationVersionFromParameters(app, version, events, parameterMap); } } if (version != null) { templateVariables.put("version", version); } templateVariables.put("application", app); createHashTypes(templateVariables, version != null ? version.getArchive() : null); return events; } private void validateStorageConfiguration(Map<Object, Object> templateVariables, List<ProcessingEvent> events) { String storagePathErrors = modelManager.getGlobalSettings().validateTemporaryStoragePath(); if (storagePathErrors != null) { events.add(new MessagesEvent( "WARNING: The archive storage path is not set and file uploads will not be processed. The archive storage path can be set on the settings page.")); templateVariables.put(FormConstants.ENCODING_TYPE, ""); } else { templateVariables.put(FormConstants.ENCODING_TYPE, "enctype=\"" + FormConstants.ENCTYPE_MULTIPART_FORMDATA + "\""); } } private Boolean canUserModifyOrCreate(Application app, ApplicationVersion version) { // we don't want to pass it back, but the // Authorizer needs the Application object // to determine whether the user may create // a version or not. version = version != null ? version : new ApplicationVersion(); version.setApplication(app); Boolean mayCreateVersion = modelManager.getAuthorizer().may(Authorizer.Action.CREATE, version); Boolean mayModifyVersion = modelManager.getAuthorizer().may(Authorizer.Action.MODIFY, version); return (mayCreateVersion || (mayModifyVersion && version != null)); } /** * Creates the list of selectable hashes * @param vars * @param archive */ @SuppressWarnings("unchecked") private void createHashTypes(Map<Object, Object> vars, ApplicationArchive archive) { List<Option> opts = new ArrayList<Option>(); String archiveHashAlg = archive != null ? archive.getHashAlgorithm() : null; HashAlgorithm alg = null; for (HashAlgorithm thisAlg : HashAlgorithm.values()) { Option newOpt = new Option(); newOpt.setIsSelected(archiveHashAlg != null && thisAlg.value().equals(archiveHashAlg)); newOpt.setInnerText(thisAlg.value()); newOpt.setValue(thisAlg.value()); opts.add(newOpt); } vars.put("hashTypes", opts); } /** * @param app * @param appId * @param events * @param parameterMap * @return The application version indicated by the parameterMap, or null */ private ApplicationVersion obtainExistingApplicationVersionFromParameters(Application app, Long appId, List<ProcessingEvent> events, Map<Object, Object> parameterMap) { // if we're not processing and there is a versionId or an identifier in the request // then we're pre-populating the form with information from the version ApplicationVersion version = null; String versionId = firstValue("versionId", parameterMap); String identifier = firstValue("identifier", parameterMap); if (StringUtils.isNotBlank(versionId) || StringUtils.isNotBlank(identifier)) { if (StringUtils.isNotBlank(versionId)) { version = modelManager.getModelService().findByPrimaryKey(ApplicationVersion.class, Long.valueOf(versionId)); } if (version == null && StringUtils.isNotBlank(identifier)) { version = modelManager.getModelService().findAppVersionByNameAndId(app.getName(), identifier); } if (version == null) { events.add(new GenericProcessingEvent(ProcessingTargets.MESSAGES, "An Application Version matching input could not be found. Creating a new version.")); } else if (version.getApplication() != null && version.getApplication().getId().compareTo(appId) != 0) { version = null; events.add(new GenericProcessingEvent(ProcessingTargets.MESSAGES, "The Application Version with id " + versionId + " is not a version of the Application with id " + appId)); } } return version; } private void processApplicationVersionFromParameters(Application app, ApplicationVersion version, List<ProcessingEvent> events, Map<Object, Object> parameterMap) { // a version is not being modified, // then create a new archive for it. if (version.getPk() == null) { version.setArchive(new ApplicationArchive()); version.getArchive().setApplication(app); version.setApplication(app); } fillInApplicationVersionFromParameters(app, version, events, parameterMap); if (version != null && version.getArchive() == null) { events.add(new MessagesEvent("Application archive could not be created. Not creating empty version.")); } else { try { modelManager.begin(); version.setLastModifier(firstValue("userPrincipalName", parameterMap)); ApplicationArchive savedArchive = version.getArchive(); version.setArchive(null); savedArchive = modelManager.addModify(savedArchive, events); version.setArchive(savedArchive); version = modelManager.addModify(version, events); app.addVersion(version); app = modelManager.addModify(app, events); modelManager.commit(events); modelManager.refresh(app, events); events.add(new MessagesEvent("Application version successfully created/modified!")); } catch (InvalidPropertiesException ipe) { modelManager.rollback(); logger.error("Unable to add/modify version " + version.getIdentifier(), ipe); events.add(new MessagesEvent("Unable to add/modify version - " + ipe.getMessage())); } catch (PersistenceException pe) { modelManager.rollback(); logger.error("Unable to add/modify version " + version.getIdentifier(), pe); events.add(new MessagesEvent("Unable to add/modify version - " + pe.getMessage())); } } } private void fillInApplicationVersionFromParameters(Application app, ApplicationVersion version, List<ProcessingEvent> events, Map<Object, Object> parameterMap) { version.setIdentifier(firstValue("identifier", parameterMap)); if (version.getArchive() == null) { version.setArchive(new ApplicationArchive()); version.getArchive().setApplication(app); } version.setApplication(app); version.setNotes(firstValue("notes", parameterMap)); Boolean archiveUncreated = true; // if there was an uploadArchive, then attempt to auto-assemble the rest of parameters if (parameterMap.get("uploadArchive") != null) { if (!(parameterMap.get("uploadArchive") instanceof FileItem)) { events.add(new MessagesEvent( "Uploaded file not processed! Is the archive storage path set in settings?")); } else { FileItem item = (FileItem) parameterMap.get("uploadArchive"); Long size = item.getSize(); if (size > 0) { try { File tempFile = ServletUtils.tempFileFromFileItem( modelManager.getGlobalSettings().getTemporaryStoragePath(), item); ApplicationArchive archive = version.getArchive(); archive.setNewFileUploaded(tempFile.getAbsolutePath()); archiveUncreated = false; } catch (Exception ioe) { logger.error("An error transpired creating an uploadArchive temp file: {}", ioe); events.add(new MessagesEvent(ioe.getMessage())); return; } finally { item.delete(); } } else { events.add(new MessagesEvent( "Uploaded file not processed! Is the archive storage path set in settings?")); } } } // else there was no zip archive uploaded if (archiveUncreated) { ApplicationArchive archive = version.getArchive(); archive.setHashAlgorithm(firstValue("hashType", parameterMap)); archive.setHash(firstValue("hash", parameterMap)); ApplicationArchive arch = modelManager.getModelService().findApplicationArchiveByHashAndAlgorithm(app, archive.getHash(), archive.getHashAlgorithm()); if (arch != null) { version.setArchive(arch); archive = arch; } archive.setUrl(firstValue("url", parameterMap)); if (notEmpty("bytesLength", parameterMap)) { archive.setBytesLength(Integer.valueOf(firstValue("bytesLength", parameterMap))); } if (notEmpty("bytesLengthUncompressed", parameterMap)) { archive.setBytesLengthUncompressed( Integer.valueOf(firstValue("bytesLengthUncompressed", parameterMap))); } } } // ACCESSORS public void setModelManager(ModelManager modelManager) { this.modelManager = modelManager; } public ModelManager getModelManager() { return modelManager; } }