/*******************************************************************************
* Copyright (c) 2006-2010 Vienna University of Technology,
* Department of Software Technology and Interactive Systems
*
* All rights reserved. This program and the accompanying
* materials are made available under the terms of the
* Apache License, Version 2.0 which accompanies
* this distribution, and is available at
* http://www.apache.org/licenses/LICENSE-2.0
*******************************************************************************/
package eu.planets_project.pp.plato.action.project;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.persistence.EntityManager;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Out;
import org.jboss.seam.annotations.RaiseEvent;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.faces.FacesMessages;
import org.xml.sax.SAXException;
import eu.planets_project.pp.plato.action.IProjectExport;
import eu.planets_project.pp.plato.action.interfaces.IProjectImport;
import eu.planets_project.pp.plato.model.Plan;
import eu.planets_project.pp.plato.model.User;
import eu.planets_project.pp.plato.model.tree.LibraryTree;
import eu.planets_project.pp.plato.model.tree.TemplateTree;
import eu.planets_project.pp.plato.util.FileUtils;
import eu.planets_project.pp.plato.util.OS;
import eu.planets_project.pp.plato.util.PlatoLogger;
import eu.planets_project.pp.plato.xml.LibraryExport;
import eu.planets_project.pp.plato.xml.ProjectExporter;
import eu.planets_project.pp.plato.xml.ProjectImporter;
/**
* Performs uploads of project- and template-XML files,
* and provides a download stream of the XML representation of the selected project.
* Creates a copy of the selected project.
* It uses {@link ProjectExporter} and {@link ProjectImporter} for creating the XML representation
* of projects and templates and vice versa.
*
* @author Michael Kraxner
*
*/
@Name("xml")
@Scope(ScopeType.SESSION)
public class XmlAction implements Serializable {
@In
private User user;
/**
*
*/
private static final long serialVersionUID = -4420194450910349095L;
private static final Log log = PlatoLogger.getLogger(XmlAction.class);
@In(required=false)
Plan selectedPlan;
/**
* XML-data which is read in {@link #doImport()}
*/
@In(required=false)
@Out(required=false)
private String input = "";
@In(required=false)
@Out(required=false)
private String directory = "";
@In(required=false)
@Out(required=false)
private String templateName;
@In(required=false)
@Out(required=false)
private String templateLibrary;
@In(required=false)
@Out(required=false)
private String templateDescription;
/**
* Reference to the fragments tree
*/
@Out(required = false)
private TemplateTree fragmentRoot;
/**
* Reference to the template tree
*/
@Out(required = false)
private TemplateTree templateRoot;
public String getDirectory() {
return directory;
}
public void setDirectory(String directory) {
this.directory = directory;
}
public void importFromDir() {
File f = new File(directory);
if (f == null || !f.isDirectory()) {
FacesMessages.instance().add(FacesMessage.SEVERITY_ERROR,
"You have to select a valid server side path for import.");
return;
}
int count = projectImport.importAllProjectsFromDir(f);
FacesMessages.instance().add(FacesMessage.SEVERITY_INFO,
"IMPORTED "+count+" PROJECTS!");
}
@In
EntityManager em;
@In(required = false)
private String fileName;
/**
* XML-data for file-upload
*/
@In(required = false)
@Out(required=false)
private byte[] file;
@In(create=true)
private IProjectImport projectImport;
public void setEm(EntityManager em) {
this.em = em;
}
public byte[] getFile() {
return file;
}
public void setFile(byte[] file) {
this.file = file;
}
/**
* Imports the XML-data of <code>input</code> and stores the projects in the database.
*/
public String doImport() {
try {
List<Plan> plans = projectImport.importProjects(new ByteArrayInputStream(input.getBytes("UTF-8")));
/*
* store all projects
*/
storeProjects(plans);
} catch (Exception e) {
log.info(e);
}
return null;
}
public String uploadPlans() {
return uploadPlans(false);
}
/**
* Imports the uploaded XML-data and stores the projects in the database.
*/
@RaiseEvent("projectListChanged")
public String uploadPlans(boolean changeUser) {
if (file == null || file.length == 0 ) {
log.debug("No file for import selected.");
FacesMessages.instance().add(FacesMessage.SEVERITY_ERROR,
"You have to select a valid XML file for import, conforming to a Plato XML schema.");
return null;
}
boolean impResult = false;
log.debug("FileName: " + fileName);
//log.debug("Length of File: " + file.length);
try {
List<Plan> plans = projectImport.importProjects(new ByteArrayInputStream(file));
int numOfProjects = plans.size();
// if the plans are imported by a NORMAL USER in the web interface, they will be
// assigned to this user, i.e. the owner is set to the current user.
// If they are imported by an ADMIN, they stay property of the original user,
// unless the admin uses a different button
if (!user.isAdmin() || changeUser) {
for (Plan p: plans) {
p.getPlanProperties().setOwner(user.getUsername());
}
}
/*
* store all projects and removes them from the list
*/
storeProjects(plans);
FacesMessages.instance().add(FacesMessage.SEVERITY_INFO, "Successfully imported " + numOfProjects + " projects!");
if (!projectImport.getAppliedTransformations().isEmpty()) {
StringBuffer msg = new StringBuffer();
msg.append("The following transformations have been applied:<br/><br/>");
msg.append("<ul>");
for (String xsl : projectImport.getAppliedTransformations()) {
msg.append("<li>").append("<a href='../xslt/"+xsl+"' target='_blank'>"+xsl+"</a>").append("</li>");
}
msg.append("</ul>");
FacesMessages.instance().add(new FacesMessage(FacesMessage.SEVERITY_INFO, "Your XML file was outdated, therefore it had to be migrated to the current Plato XML format.", msg.toString()));
}
impResult = true;
} catch (Exception e) {
/*
* Import failed, do not reload the project list
*/
log.debug("Import failed:" + e.getMessage());
FacesMessages.instance().add(FacesMessage.SEVERITY_ERROR,
"You have to select a valid XML file for import, conforming to a Plato XML schema.");
}
this.file = null;
this.fileName = "";
if (impResult)
return "success";
else
return null;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
/**
* Imports the uploaded XML-Files and stores the templates in the database.
*/
@RaiseEvent("projectListChanged")
public String uploadTemplates() {
try {
projectImport.storeTemplatesInLibrary(file);
fragmentRoot = null;
templateRoot = null;
} catch (SAXException e) {
e.printStackTrace();
FacesMessages.instance().add(FacesMessage.SEVERITY_ERROR, "Error importing templates. " + e.getMessage());
return null;
} catch (IOException e) {
FacesMessages.instance().add(FacesMessage.SEVERITY_ERROR, "Error importing templates. " + e.getMessage());
e.printStackTrace();
return null;
}
FacesMessages.instance().add(FacesMessage.SEVERITY_INFO, "Templates successfully imported");
return null;
}
/**
* This method takes the objective tree from the selected plan, creates a template out of it and
* stores it in the template library.
*
* The admin can choose the
* * templateLibrary, e.g. "Public Templates"
* * templateName, i.e. the name of the root node, which is the name of the template
* * templateDescription
*
* Also see: {@link XmlAction#storeTemplatesInLibrary(byte[])}
*
*
* @return
*/
public String addTreeToTemplateLibrary() {
if (selectedPlan == null) {
FacesMessages.instance().add(FacesMessage.SEVERITY_ERROR, "Please select a plan first.");
return null;
}
ProjectExporter exporter = new ProjectExporter();
String xml = exporter.getObjectiveTreeAsTemplate(selectedPlan, templateLibrary, templateName, templateDescription);
try {
projectImport.storeTemplatesInLibrary(xml.getBytes("UTF-8"));
fragmentRoot = null;
templateRoot = null;
} catch (Exception e) {
log.error(e);
FacesMessages.instance().add(FacesMessage.SEVERITY_ERROR, "Error importing tree as templates. " + e.getMessage());
return null;
}
FacesMessages.instance().add(FacesMessage.SEVERITY_INFO, "Successfully imported tree as template.");
return null;
}
/**
* Creates a copy of the selected project and stores it in the database.
* To create the copy the project is first exported, then imported as a new project.
*/
public String cloneProject() {
if (selectedPlan == null) {
FacesMessages.instance().add(FacesMessage.SEVERITY_ERROR, "No project selected - please load project first.");
log.error("No project selected - please load project first.");
return null;
}
File tempFile = new File(OS.getTmpPath() + "cloneplans_" + System.currentTimeMillis() + ".xml");
tempFile.deleteOnExit();
ProjectExporter exporter = new ProjectExporter();
try {
exporter.exportToFile(selectedPlan, tempFile);
List<Plan> plans = projectImport.importProjects(new FileInputStream(tempFile));
/*
* store project
*/
storeProjects(plans);
FacesMessages.instance().add(FacesMessage.SEVERITY_INFO, "Plan '" + selectedPlan.getPlanProperties().getName() +"' successfully cloned.");
log.debug("Plan '" + selectedPlan.getPlanProperties().getName() +"' successfully cloned.");
} catch (Exception e) {
FacesMessages.instance().add(FacesMessage.SEVERITY_ERROR, "Could not clone project: '" + selectedPlan.getPlanProperties().getName() + "'.");
log.debug("Could not clone project: '" + selectedPlan.getPlanProperties().getName() + "'.", e);
}
tempFile.delete();
return null;
}
@In(create=true)
IProjectExport projectExport;
public String export() {
if (selectedPlan != null) {
// convert project-name to a filename, add date:
SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd_kkmmss");
String planName = selectedPlan.getPlanProperties().getName();
if ((planName == null) || "".equals(planName)) {
planName = "export";
}
String normalizedPlanName = FileUtils.makeFilename(planName);
String filename = normalizedPlanName + "-" + formatter.format(new Date());
String binarydataTempPath = OS.getTmpPath() + normalizedPlanName + System.currentTimeMillis() + "/";
File binarydataTempDir = new File(binarydataTempPath);
binarydataTempDir.mkdirs();
try {
HttpServletResponse response = (HttpServletResponse)FacesContext.getCurrentInstance().getExternalContext().getResponse();
response.setContentType("application/x-download");
response.setHeader("Content-Disposition", "attachement; filename=\""+filename+".xml\"");
// the length of the resulting XML file is unknown due to formatting: response.setContentLength(xml.length());
try {
BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
projectExport.exportComplete(selectedPlan.getPlanProperties().getId(), out, binarydataTempPath);
out.flush();
out.close();
} catch (IOException e) {
FacesMessages.instance().add(FacesMessage.SEVERITY_ERROR, "An error occured while generating the export file.");
log.error("Could not open response-outputstream: ", e);
}
FacesContext.getCurrentInstance().responseComplete();
} finally {
OS.deleteDirectory(binarydataTempDir);
}
}
System.gc();
return null;
}
/**
* Stores projects in the database.
*
* @param plans
*/
private void storeProjects(List<Plan> plans) {
/*
* store all projects
*/
while(!plans.isEmpty()) {
Plan plan = plans.get(0);
em.persist(plan);
em.flush();
plans.remove(plan);
plan = null;
em.clear();
System.gc();
}
}
public String getInput() {
return input;
}
public void setInput(String input) {
this.input = input;
}
public String uploadLibrary() {
if (file == null || file.length == 0 ) {
log.debug("No file for import selected.");
FacesMessages.instance().add(FacesMessage.SEVERITY_ERROR,
"Please select a Library-export file.");
return null;
}
LibraryExport imp = new LibraryExport();
try {
LibraryTree newLib = imp.importFromStream(new ByteArrayInputStream(file));
// at the moment we only support one Library definition
newLib.setName("mainlibrary");
// delete existing library
LibraryTree oldLib = null;
List<LibraryTree> trees = null;
trees = em.createQuery("select l from LibraryTree l where (l.name = 'mainlibrary') ").getResultList();
if ((trees != null) && (trees.size() > 0)) {
oldLib = trees.get(0);
}
em.remove(oldLib);
// em.flush();
em.persist(newLib);
} catch (Exception e) {
log.error("Failed to import Library: ", e);
FacesMessages.instance().add(FacesMessage.SEVERITY_ERROR, "An error occured while importing the library.");
}
return null;
}
public String exportLibrary() {
LibraryTree lib = null;
List<LibraryTree> trees = null;
trees = em.createQuery("select l from LibraryTree l where (l.name = 'mainlibrary') ").getResultList();
if ((trees != null) && (trees.size() > 0)) {
lib = trees.get(0);
}
if (lib != null) {
// convert project-name to a filename, add date:
SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd_kkmmss");
String filename = "RequirementsLibrary-" + formatter.format(new Date());
String binarydataTempPath = OS.getTmpPath() + "RequirementsLibrary-" + System.currentTimeMillis() + "/";
File binarydataTempDir = new File(binarydataTempPath);
binarydataTempDir.mkdirs();
LibraryExport exp = new LibraryExport();
try {
HttpServletResponse response = (HttpServletResponse)FacesContext.getCurrentInstance().getExternalContext().getResponse();
response.setContentType("application/x-download");
response.setHeader("Content-Disposition", "attachement; filename=\""+filename+".xml\"");
// the length of the resulting XML file is unknown due to formatting: response.setContentLength(xml.length());
try {
BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
exp.exportToStream(lib, out);
out.flush();
out.close();
} catch (IOException e) {
FacesMessages.instance().add(FacesMessage.SEVERITY_ERROR, "An error occured while generating the export file.");
log.error("Could not open response-outputstream: ", e);
}
FacesContext.getCurrentInstance().responseComplete();
} finally {
OS.deleteDirectory(binarydataTempDir);
}
} else {
FacesMessages.instance().add(FacesMessage.SEVERITY_INFO, "No Library found, create one first.");
}
System.gc();
return null;
}
public String getTemplateName() {
return templateName;
}
public void setTemplateName(String templateName) {
this.templateName = templateName;
}
public String getTemplateDescription() {
return templateDescription;
}
public void setTemplateDescription(String templateDescription) {
this.templateDescription = templateDescription;
}
public String getTemplateLibrary() {
return templateLibrary;
}
public void setTemplateLibrary(String templateLibrary) {
this.templateLibrary = templateLibrary;
}
}