Java tutorial
/******************************************************************************* * 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(); } }