package gr.ntua.ivml.mint.persistent; import gr.ntua.ivml.mint.db.DB; import gr.ntua.ivml.mint.persistent.DataUpload.EntryProcessor; import java.io.BufferedOutputStream; import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Writer; import java.util.Date; import java.util.Enumeration; import org.apache.commons.io.IOUtils; import org.apache.log4j.Logger; import org.hibernate.Hibernate; import de.schlichtherle.util.zip.BasicZipFile; import de.schlichtherle.util.zip.ZipEntry; import de.schlichtherle.util.zip.ZipOutputStream; public class Transformation implements Lockable { public static final Logger log = Logger.getLogger(Transformation.class ); Long dbID; Date beginTransform, endTransform; User user; DataUpload dataUpload; Mapping mapping; BlobWrap zippedOutput; int statusCode; String statusMessage; XmlObject parsedOutput; String jsonMapping; String report; // transient, contains a zip archive with all the output files File tmpFile; MyZipOutputStream zippedOutputStream; int runningOutputNumber; public static final int OK = 0; public static final int ERROR = -1; public static final int IDLE = 1; public static final int WRITING = 2; public static final int UPLOADING = 3; public static final int INDEXING = 4; public static final int DUMMY = 5; /** * * @author Arne Stabenau * Overwrite close so that users of the class can close their stream without closing * the ZipOutputStream. */ public static class MyZipOutputStream extends ZipOutputStream { public MyZipOutputStream(OutputStream out) { super(out); } public void close() throws IOException { super.closeEntry(); } public void finished() throws IOException { super.close(); } } public Long getDbID() { return dbID; } public void setDbID(Long dbID) { this.dbID = dbID; } /** * If the json string changed, the transformation is stale ... * @return */ public boolean isStale() { if(getMapping().getJsonString()!=null){ try { return !getMapping().getJsonString().equals(getJsonMapping()); } catch( Exception e ) { log.error( "on is stale check", e ); } } return false; } public String getReport() { return report; } public void setReport(String report) { this.report = report; } public String getJsonMapping() { return jsonMapping; } public void setJsonMapping(String jsonMapping) { this.jsonMapping = jsonMapping; } public XmlObject getParsedOutput() { return parsedOutput; } public void setParsedOutput(XmlObject parsedOutput) { this.parsedOutput = parsedOutput; } public Date getBeginTransform() { return beginTransform; } public void setBeginTransform(Date beginTransform) { this.beginTransform = beginTransform; } public Date getEndTransform() { return endTransform; } public void setEndTransform(Date endTransform) { this.endTransform = endTransform; } public User getUser() { return user; } public void setUser(User user) { this.user = user; } public DataUpload getDataUpload() { return dataUpload; } public void setDataUpload(DataUpload dataUpload) { this.dataUpload = dataUpload; } public Mapping getMapping() { return mapping; } public void setMapping(Mapping mapping) { this.mapping = mapping; } public BlobWrap getZippedOutput() { return zippedOutput; } public void setZippedOutput(BlobWrap zippedOutput) { this.zippedOutput = zippedOutput; } public int getStatusCode() { return statusCode; } public void setStatusCode(int statusCode) { this.statusCode = statusCode; } public String getStatusMessage() { return statusMessage; } public void setStatusMessage(String statusMessage) { this.statusMessage = statusMessage; } /** * Call this before you start appending output. It creates a standard XML header, * since the Transformations are not having one any more! */ public void startOutput() { try { tmpFile = File.createTempFile("MintOutput", ".xml.zip"); zippedOutputStream = new MyZipOutputStream(new FileOutputStream(tmpFile)); ZipEntry entry = new ZipEntry("Transformation_"+getDbID()+"/"); zippedOutputStream.putNextEntry(entry); setStatusCode(Transformation.WRITING); } catch( Exception e ) { setStatusCode(ERROR); setStatusMessage(e.getMessage()); log.error( "Couldnt open tmp file for Transformation output"); } } /** * Output to here * @param output */ public void appendOutput( String output ) { try { setStatusCode( WRITING ); zippedOutputStream.write(output.getBytes("UTF8")); } catch( IOException ie ) { log.error( "Can't append output, now thats wired" ); setStatusCode(ERROR); setStatusMessage( ie.getMessage() ); } } /** * Move output to next file, filenames are numbered. */ public void nextOutputFile() throws Exception { ZipEntry entry =new ZipEntry( "Transformation_" + getDbID() + "/Output_"+runningOutputNumber+".xml" ); zippedOutputStream.putNextEntry(entry ); runningOutputNumber += 1 ; } /** * Get a writer to the output file. You have to close it before you declare the * output finished !! * @return */ public Writer getWriterToOutput() { Writer result = null; try { setStatusCode(WRITING); OutputStreamWriter osw = new OutputStreamWriter(zippedOutputStream, "UTF8"); result = new BufferedWriter( osw ); } catch( Exception e ) { log.error( "Desaster", e ); } return result; } public OutputStream getStreamToOutput() { OutputStream result = null; try { setStatusCode(WRITING); result = new BufferedOutputStream(zippedOutputStream); } catch( Exception e ) { log.error( "Desaster", e ); } return result; } /** * Closes the output and writes it to database */ public void finishOutput() { try { // gzipping the tmp file zippedOutputStream.finished(); setStatusCode(UPLOADING); DB.commit(); // uploading into the db if( zippedOutput != null ) { DB.getSession().delete(zippedOutput); } zippedOutput = new BlobWrap(); zippedOutput.data = Hibernate.createBlob( new FileInputStream( tmpFile ), (int) tmpFile.length()); DB.commit(); log.debug( "Successfully written output to DB"); } catch( IOException ie ) { log.error( "finishing output failed" ); setStatusCode(ERROR); setStatusMessage(ie.getMessage()); DB.commit(); } } /** * Returns a stream to a zip archive. * @return */ public InputStream getDownloadStream() { InputStream is = null; if( tmpFile == null ) unloadToTmpFile(); try { is = new FileInputStream(tmpFile); } catch( Exception e ) { log.error( "File unload problem", e); } return is; } public void processAllEntries( EntryProcessor ep ) throws Exception { InputStream is = null; de.schlichtherle.util.zip.ZipEntry ze = null; if( tmpFile == null ) unloadToTmpFile(); try { BasicZipFile bz = new BasicZipFile( tmpFile ); Enumeration entries = bz.entries(); while( entries.hasMoreElements() ) { ze = (de.schlichtherle.util.zip.ZipEntry) entries.nextElement(); InputStream zis = bz.getInputStream(ze); // log.debug( "Processing " + ze.getName()); try { ep.processEntry(ze, zis); } catch( Exception e ) { log.error( "Problematic file: " + ze.getName(), e ); throw e; } } } catch( Exception e ) { log.error( "Problem reading output", e ); } finally { try { is.close(); } catch( Exception e ){}; } } public void unloadToTmpFile() { try { tmpFile = File.createTempFile("unloadOutput", ".zip"); tmpFile.deleteOnExit(); log.info( "Unloading to " + tmpFile.getAbsolutePath()); FileOutputStream fos = new FileOutputStream( tmpFile ); BufferedOutputStream bos = new BufferedOutputStream( fos,4096 ); InputStream is = getZippedOutput().getData().getBinaryStream(); IOUtils.copy(is, bos); is.close(); bos.flush(); bos.close(); DB.commit(); } catch( Exception e ) { log.error( "Cannot copy BLOB to tmp file", e ); } } /** * If you are finished reading the output, it would be nice to * remove the tmp file .. */ public void clearTmpFile() { if( tmpFile != null ) { tmpFile.delete(); tmpFile = null; } } /** * You can use this after you unloadToTmpFile() * @return */ public File getTmpFile() { return tmpFile; } @Override public String getLockname() { // TODO Auto-generated method stub String result = ""; if( mapping != null ) result += mapping.getName(); result += " on "; if( getDataUpload() != null ) result += getDataUpload().getUploadDate().toString() + " upload."; return result; } /** * For Annotations that don't belong to anything, we like to have a fake * Transformation, a dummy. */ public boolean isDummy() { return getStatusCode() == Transformation.DUMMY; } public static Transformation createDummy( User u ) { Transformation result = new Transformation(); result.setMapping(null); result.setDataUpload(DataUpload.createDummy( u )); result.setStatusCode(Transformation.DUMMY); result.setUser( u ); return result; } }