package org.ops4j.pax.exam.quickbuild.internal; import java.io.File; import java.io.IOException; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; import java.io.PipedInputStream; import java.io.PipedOutputStream; import java.net.URI; import java.util.HashMap; import java.util.Map; import java.util.jar.JarEntry; import java.util.jar.JarOutputStream; import com.google.inject.Inject; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.ops4j.io.StreamUtils; import org.ops4j.pax.exam.quickbuild.Quickbuild; import org.ops4j.pax.exam.quickbuild.QType; import org.ops4j.pax.exam.quickbuild.Snapshot; import org.ops4j.pax.exam.quickbuild.SnapshotElement; import org.ops4j.store.Handle; import org.ops4j.store.Store; /** * */ public class DefaultQuickbuild implements Quickbuild { public static final Log LOGGER = LogFactory.getLog( DefaultQuickbuild.class ); final private Store<InputStream> m_store; @Inject public DefaultQuickbuild( Store<InputStream> store ) { m_store = store; } public InputStream update( Snapshot referenceSnapshot, File changedContentFolder ) throws IOException { // keep it very simple for now final Map<String, Handle> folderContent = new HashMap<String, Handle>(); deflateFolder( folderContent, changedContentFolder.getCanonicalPath(), changedContentFolder ); // pack jar from work into anchor final PipedInputStream pin = new PipedInputStream(); final PipedOutputStream pout = new PipedOutputStream( pin ); final Map<String, URI> contentMap = calculateNewJarContent( referenceSnapshot, folderContent ); new Thread() { @Override public void run() { try { pack( contentMap, pout ); } catch( IOException e ) { e.printStackTrace(); } finally { try { pout.close(); } catch( IOException e ) { // } } } }.start(); // TODO replace with IOPipes after this somehow works. return pin; //return new FileInputStream( new File( changedContentFolder, "build_by_quickbuild.jar" ) ); } private void deflateFolder( Map<String, Handle> contentMap, String base, File folder ) throws IOException { for( File f : folder.listFiles() ) { if( !f.isHidden() && f.isDirectory() ) { deflateFolder( contentMap, base, f ); } else if( !f.isHidden() && f.getName().endsWith( ".class" ) ) { String p = f.getCanonicalPath().replaceAll( "\\\\", "/" ); // cut prefix p = p.substring( base.length() + 1 ); contentMap.put( p, m_store.store( new FileInputStream( f ) ) ); } } } /** * Just pack fully resolved resources into an outputstream. * * @param agg * @param out */ public void pack( Map<String, URI> agg, OutputStream out ) throws IOException { // calculate the final map JarOutputStream jout = new JarOutputStream( out ); try { // first set manifest if available: URI manifest = agg.get( "META-INF/MANIFEST.MF" ); if( manifest != null ) { JarEntry entry = new JarEntry( "META-INF/MANIFEST.MF" ); jout.putNextEntry( entry ); StreamUtils.copyStream( manifest.toURL().openStream(), jout, false ); jout.closeEntry(); } for( String name : agg.keySet() ) { if( name.equals( "META-INF/MANIFEST.MF" ) ) { continue; } JarEntry entry = new JarEntry( name ); jout.putNextEntry( entry ); StreamUtils.copyStream( agg.get( name ).toURL().openStream(), jout, false ); jout.closeEntry(); } } finally { jout.close(); } } private Map<String, URI> calculateNewJarContent( Snapshot snapshot, Map<String, Handle> contentMap ) throws IOException { Map<String, URI> agg = new HashMap<String, URI>(); int files_new = 0; int files_changed = 0; int files_removed = 0; for( SnapshotElement snapshotElement : snapshot ) { if( !contentMap.containsKey( snapshotElement.name() ) ) { if( snapshotElement.type() == QType.OWN ) { // removed ! files_removed++; } else { // external, still include agg.put( snapshotElement.name(), snapshotElement.reference() ); } } else { // exlude the ones we do not had initially, too. Add the rest of cause. if( snapshotElement.type() != QType.EXCLUDED ) { // if wanted, add this if( snapshotElement.type() == QType.OWN && !checkSumsEquals( snapshotElement, contentMap.get( snapshotElement.name() ) ) ) { files_changed++; } agg.put( snapshotElement.name(), m_store.getLocation( contentMap.get( snapshotElement.name() ) ) ); } // remove from content so we do not pick it up again contentMap.remove( snapshotElement.name() ); } } for( String name : contentMap.keySet() ) { // just new stuff in here agg.put( name, m_store.getLocation( contentMap.get( name ) ) ); files_new++; } if( files_changed + files_new + files_removed > 0 ) { LOGGER.info( "QUICKBUILD CHANGESET: " + files_new + " new, " + files_removed + " removed, " + files_changed + " changed." ); } else { LOGGER.info( "QUICKBUILD CHANGESET: no changes detected." ); } return agg; } private boolean checkSumsEquals( SnapshotElement snapshotElement, Handle handle ) { return snapshotElement.checksum().equals( handle.getIdentification() ); } }