eu.planets_project.pp.plato.action.ProjectExportAction.java Source code

Java tutorial

Introduction

Here is the source code for eu.planets_project.pp.plato.action.ProjectExportAction.java

Source

/*******************************************************************************
 * 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;

import java.io.BufferedOutputStream;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.io.Writer;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import javax.ejb.Remove;
import javax.ejb.Stateful;
import javax.faces.application.FacesMessage;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import org.apache.commons.logging.Log;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.DocumentSource;
import org.dom4j.io.XMLWriter;
import org.jboss.annotation.ejb.cache.Cache;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.Destroy;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.faces.FacesMessages;

import sun.misc.BASE64Encoder;
import eu.planets_project.pp.plato.model.ByteStream;
import eu.planets_project.pp.plato.model.DigitalObject;
import eu.planets_project.pp.plato.model.Plan;
import eu.planets_project.pp.plato.model.PlanProperties;
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.ProjectExporter;

/**
 * This class inserts test data into the persistence layer, including import of
 * objective trees from case studies.
 *
 * @author Christoph Becker
 */
@Stateful
@Scope(ScopeType.EVENT)
@Name("projectExport")
@Cache(org.jboss.ejb3.cache.NoPassivationCache.class)
public class ProjectExportAction implements Serializable, IProjectExport {

    private static final long serialVersionUID = 2155152208617526555L;

    private static final Log log = PlatoLogger.getLogger(ProjectExportAction.class);

    @PersistenceContext
    EntityManager em;

    @Remove
    @Destroy
    public void destroy() {

    }

    /**
     * Exports all projects into separate xml files and adds them to a zip archive.  
     * @return null Always returns null, so user stays on same screen after action performed
     */
    public String exportAllProjectsToZip() {
        List<PlanProperties> ppList = em.createQuery("select p from PlanProperties p").getResultList();

        if (!ppList.isEmpty()) {
            log.debug("number of plans to export: " + ppList.size());
            String filename = "allprojects.zip";

            String exportPath = OS.getTmpPath() + "export" + System.currentTimeMillis() + "/";
            new File(exportPath).mkdirs();

            String binarydataTempPath = exportPath + "binarydata/";
            File binarydataTempDir = new File(binarydataTempPath);
            binarydataTempDir.mkdirs();

            try {
                OutputStream out = new BufferedOutputStream(new FileOutputStream(exportPath + filename));
                ZipOutputStream zipOut = new ZipOutputStream(out);

                for (PlanProperties pp : ppList) {
                    log.debug("EXPORTING: " + pp.getName());
                    ZipEntry zipAdd = new ZipEntry(String.format("%1$03d", pp.getId()) + "-"
                            + FileUtils.makeFilename(pp.getName()) + ".xml");
                    zipOut.putNextEntry(zipAdd);
                    // export the complete project, including binary data
                    exportComplete(pp.getId(), zipOut, binarydataTempPath);
                    zipOut.closeEntry();
                }
                zipOut.close();
                out.close();
                new File(exportPath + "finished.info").createNewFile();

                FacesMessages.instance().add(FacesMessage.SEVERITY_INFO, "Export was written to: " + exportPath);
                log.info("Export was written to: " + exportPath);
            } catch (IOException e) {
                FacesMessages.instance().add(FacesMessage.SEVERITY_ERROR,
                        "An error occured while generating the export file.");
                log.error("An error occured while generating the export file.", e);
                File errorInfo = new File(exportPath + "error.info");
                try {
                    Writer w = new FileWriter(errorInfo);
                    w.write("An error occured while generating the export file:");
                    w.write(e.getMessage());
                    w.close();
                } catch (IOException e1) {
                    log.error("Could not write error file.");
                }

            } finally {
                // remove all binary temp files
                OS.deleteDirectory(binarydataTempDir);
            }

        } else {
            FacesMessages.instance().add("No Projects found!");
        }
        return null;
    }

    /**
     * Exports the project identified by PlanProperties.Id ppid and writes the document
     * to the given OutputStream - including all binary data.
     * (currently required by {@link #exportAllProjectsToZip()} )
     * - Does NOT clean up temp files written to baseTempPath
     * 
     * @param ppid
     * @param out
     * @param baseTempPath used to write temp files for binary data, 
     *        must not be used by other exports at the same time
     */
    public void exportComplete(int ppid, OutputStream out, String baseTempPath) {
        BASE64Encoder encoder = new BASE64Encoder();
        ProjectExporter exporter = new ProjectExporter();
        Document doc = exporter.createProjectDoc();

        //        int i = 0;
        List<Plan> list = null;
        try {
            list = em.createQuery("select p from Plan p where p.planProperties.id = " + ppid).getResultList();
        } catch (Exception e1) {
            list = new ArrayList<Plan>();
            FacesMessages.instance().add(FacesMessage.SEVERITY_ERROR,
                    "An error occured while generating the export file.");
            log.error("Could not load planProperties: ", e1);
        }
        try {
            if (list.size() != 1) {
                FacesMessages.instance().add(FacesMessage.SEVERITY_ERROR,
                        "Skipping the export of the plan with properties" + ppid + ": Couldnt load.");
            } else {
                //log.debug("adding project "+p.getplanProperties().getName()+" to XML...");
                String tempPath = baseTempPath;
                File tempDir = new File(tempPath);
                tempDir.mkdirs();

                List<Integer> uploadIDs = new ArrayList<Integer>();
                List<Integer> recordIDs = new ArrayList<Integer>();
                try {
                    exporter.addProject(list.get(0), doc, uploadIDs, recordIDs);

                    writeBinaryObjects(recordIDs, uploadIDs, tempPath, encoder);
                    // perform XSLT transformation to get the DATA into the PLANS
                    XMLWriter writer = new XMLWriter(
                            new FileOutputStream("/tmp/testout" + System.currentTimeMillis() + ".xml"));
                    writer.write(doc);
                    writer.close();
                    addBinaryData(doc, out, tempPath);
                } 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);
                } catch (TransformerException e) {
                    FacesMessages.instance().add(FacesMessage.SEVERITY_ERROR,
                            "An error occured while generating the export file.");
                    log.error(e);
                }
            }
        } finally {
            /* clean up */
            list.clear();
            list = null;

            em.clear();
            System.gc();
        }

    }

    /**
     *  Performs XSLT transformation to get the DATA into the PLANS
     */
    private void addBinaryData(Document doc, OutputStream out, String aTempDir) throws TransformerException {
        URL xslPath = Thread.currentThread().getContextClassLoader().getResource("data/xslt/bytestreams.xsl");
        InputStream xsl = Thread.currentThread().getContextClassLoader()
                .getResourceAsStream("data/xslt/bytestreams.xsl");

        TransformerFactory transformerFactory = TransformerFactory.newInstance();
        Transformer transformer = transformerFactory.newTransformer(new StreamSource(xsl));
        transformer.setParameter("tempDir", aTempDir);

        Source xmlSource = new DocumentSource(doc);

        Result outputTarget = new StreamResult(out); //new FileWriter(outFile));

        log.debug("starting bytestream transformation ...");
        transformer.transform(xmlSource, outputTarget);
        log.debug("FINISHED bytestream transformation!");
    }

    /**
     * Adds all enlisted plans to an XML document, but does NOT write binary data.
     * Instead the Id's of all referenced uploads and sample records are added to the provided lists,
     * this way they can be added later.
     *   
     * @param ppids
     * @param uploadIDs
     * @param recordIDs
     * @return
     */
    public Document exportToXml(List<Integer> ppids, List<Integer> uploadIDs, List<Integer> recordIDs) {
        ProjectExporter exporter = new ProjectExporter();
        Document doc = exporter.createProjectDoc();

        int i = 0;
        for (Integer id : ppids) {
            // load one plan after the other:
            List<Plan> list = em.createQuery("select p from Plan p where p.planProperties.id = " + id)
                    .getResultList();
            if (list.size() != 1) {
                FacesMessages.instance().add(FacesMessage.SEVERITY_ERROR,
                        "Skipping the export of the plan with properties" + id + ": Couldnt load.");
            } else {
                //log.debug("adding project "+p.getplanProperties().getName()+" to XML...");
                exporter.addProject(list.get(0), doc, uploadIDs, recordIDs);
            }
            list.clear();
            list = null;

            log.info("XMLExport: addString destinationed project ppid=" + id);
            i++;
            if ((i % 10 == 0)) {
                em.clear();
                System.gc();
            }
        }
        return doc;
    }

    /**
     * Loads all binary data for the given samplerecord- and upload Ids and dumps it to XML files,  located in tempDir
     *       
     * @param recordIDs
     * @param uploadIDs
     * @param tempDir
     * @param encoder
     * @throws IOException
     */
    private void writeBinaryObjects(List<Integer> recordIDs, List<Integer> uploadIDs, String aTempDir,
            BASE64Encoder encoder) throws IOException {
        int counter = 0;
        int skip = 0;
        List<Integer> allIDs = new ArrayList<Integer>();
        allIDs.addAll(recordIDs);
        allIDs.addAll(uploadIDs);
        log.info("writing XMLs for bytestreams of digital objects. Size = " + allIDs.size());
        for (Integer id : allIDs) {
            if (counter > 200 * 1024 * 1024) { // 200 MB unused stuff lying around
                System.gc();
                counter = 0;
            }
            DigitalObject object = em.find(DigitalObject.class, id);
            if (object.isDataExistent()) {
                counter += object.getData().getSize();
                File f = new File(aTempDir + object.getId() + ".xml");
                writeBinaryData(id, object.getData(), f, encoder);
            } else {
                skip++;
            }
            object = null;
        }
        em.clear();
        System.gc();
        log.info("finished writing bytestreams of digital objects. skipped empty objects: " + skip);
    }

    /**
     * Dumps binary data to provided file
     * It results in an XML file with a single element: data, 
     * @param id
     * @param data
     * @param f
     * @param encoder
     * @throws IOException
     */
    private static void writeBinaryData(int id, ByteStream data, File f, BASE64Encoder encoder) throws IOException {
        Document streamDoc = DocumentHelper.createDocument();
        Element d = streamDoc.addElement("data");
        d.addAttribute("id", "" + id);
        d.setText(encoder.encode(data.getData()));
        XMLWriter writer = new XMLWriter(new BufferedWriter(new FileWriter(f)), ProjectExporter.prettyFormat);
        writer.write(streamDoc);
        writer.flush();
        writer.close();
    }

}