Author: saces
Date: 2009-04-14 18:42:36 +0000 (Tue, 14 Apr 2009)
New Revision: 26788

Added:
   trunk/freenet/src/freenet/client/async/ContainerInserter.java
Log:
new class ContainerInserter - insert a ManifestElements as container
TODOs:
  fix resolve metadata
    -how to format the redirects/submetadata?
    -the current metadataMap thingy s***s, invent a working one ;)
  persistence
  add a MAX_SIZE for the final container 

Added: trunk/freenet/src/freenet/client/async/ContainerInserter.java
===================================================================
--- trunk/freenet/src/freenet/client/async/ContainerInserter.java               
                (rev 0)
+++ trunk/freenet/src/freenet/client/async/ContainerInserter.java       
2009-04-14 18:42:36 UTC (rev 26788)
@@ -0,0 +1,391 @@
+/**
+ * 
+ */
+package freenet.client.async;
+
+import java.io.BufferedOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map.Entry;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+import org.apache.tools.tar.TarEntry;
+import org.apache.tools.tar.TarOutputStream;
+
+import com.db4o.ObjectContainer;
+
+import freenet.client.ClientMetadata;
+import freenet.client.DefaultMIMETypes;
+import freenet.client.InsertBlock;
+import freenet.client.InsertContext;
+import freenet.client.InsertException;
+import freenet.client.Metadata;
+import freenet.client.MetadataUnresolvedException;
+import freenet.client.ArchiveManager.ARCHIVE_TYPE;
+import freenet.keys.FreenetURI;
+import freenet.support.LogThresholdCallback;
+import freenet.support.Logger;
+import freenet.support.api.Bucket;
+import freenet.support.io.BucketTools;
+
+/**
+ * pack the container/archive, then hand it off to SimpleFileInserter
+ *
+ * TODO fix resolve metadata
+ *     -how to format the redirects/submetadata?
+ *     -the current metadataMap thingy s***s, invent a working one ;)
+ * TODO persistence
+ * TODO add a MAX_SIZE for the final container(file)
+ * 
+ * @author saces
+ * 
+ */
+public class ContainerInserter implements ClientPutState {
+
+       private static volatile boolean logMINOR;
+       private static volatile boolean logDEBUG;
+
+       static {
+               Logger.registerLogThresholdCallback(new LogThresholdCallback() {
+                       
+                       @Override
+                       public void shouldUpdate() {
+                               logMINOR = Logger.shouldLog(Logger.MINOR, this);
+                               logDEBUG = Logger.shouldLog(Logger.DEBUG, this);
+                       }
+               });
+       }
+
+
+       private class ContainerElement {
+               private final Bucket data;
+               private final String targetInArchive;
+               
+               private ContainerElement(Bucket data2, String targetInArchive2) 
{
+                       data = data2;
+                       targetInArchive = targetInArchive2;
+               }
+       }
+
+       private ArrayList<ContainerElement> containerItems;
+       private HashMap<Metadata, Entry<String, Object>> metadataMap;
+
+       private final BaseClientPutter parent;
+       private final PutCompletionCallback cb;
+       private boolean cancelled;
+       private boolean finished;
+       private final boolean persistent;
+       private final HashMap<String, Object> origMetadata;
+       private final ARCHIVE_TYPE archiveType;
+       private final FreenetURI targetURI;
+       private final Object token;
+       private final boolean getCHKOnly;
+       private final boolean earlyEncode;
+       private final InsertContext ctx;
+       private final boolean reportMetadataOnly;
+       private final boolean dontCompress;
+
+       /**
+        * @param metadata2 
+        * @param archiveType2 
+        * @param targetURI2 
+        * @param token2 
+        * @param getCHKOnly2 
+        * @param earlyEncode2 
+        * @param ctx2 
+        * @param reportMetadataOnly2 
+        * 
+        */
+       public ContainerInserter(
+                       BaseClientPutter parent2, 
+                       PutCompletionCallback cb2, 
+                       HashMap<String, Object> metadata2,
+                       FreenetURI targetURI2,
+                       InsertContext ctx2,
+                       boolean dontCompress2,
+                       boolean getCHKOnly2,
+                       boolean reportMetadataOnly2,
+                       Object token2,
+                       ARCHIVE_TYPE archiveType2,
+                       boolean freeData,
+                       boolean earlyEncode2) {
+               parent = parent2;
+               cb = cb2;
+               hashCode = super.hashCode();
+               persistent = parent.persistent();
+               origMetadata = metadata2;
+               archiveType = archiveType2;
+               targetURI = targetURI2;
+               token = token2;
+               getCHKOnly = getCHKOnly2;
+               earlyEncode = earlyEncode2;
+               ctx = ctx2;
+               dontCompress = dontCompress2;
+               reportMetadataOnly = reportMetadataOnly2;
+               containerItems = new ArrayList<ContainerElement>();
+               metadataMap = new HashMap<Metadata, Entry<String, Object>>();
+       }
+
+       public void cancel(ObjectContainer container, ClientContext context) {
+               synchronized(this) {
+                       if(cancelled) return;
+                       cancelled = true;
+               }
+               if(persistent)
+                       container.store(this);
+               if(persistent)
+                       container.activate(cb, 1);
+               // Must call onFailure so get removeFrom()'ed
+               cb.onFailure(new InsertException(InsertException.CANCELLED), 
this, container, context);
+       }
+
+       public BaseClientPutter getParent() {
+               return parent;
+       }
+
+       public Object getToken() {
+               return token;
+       }
+
+       public void removeFrom(ObjectContainer container, ClientContext 
context) {
+               // TODO
+       }
+
+       public void schedule(ObjectContainer container, ClientContext context) 
throws InsertException {
+               start(container, context);
+       }
+
+
+       private void start(ObjectContainer container, ClientContext context) {
+               if(logDEBUG) Logger.debug(this, "Atempt to start a container 
inserter", new Exception("debug"));
+               
+               makeMetadata(context, container);
+               
+               synchronized(this) {
+                       if(finished) return;
+               }
+               
+               InsertBlock block;
+               try {
+                       Bucket outputBucket = 
context.getBucketFactory(persistent).makeBucket(-1);
+                       String mimeType = (archiveType == ARCHIVE_TYPE.TAR ?
+                               createTarBucket(outputBucket, container) :
+                               createZipBucket(outputBucket, container));
+                       
+                       if(logMINOR) Logger.minor(this, "We are using 
"+archiveType);
+                       
+                       // Now we have to insert the Archive we have generated.
+                       
+                       // Can we just insert it, and not bother with a 
redirect to it?
+                       // Thereby exploiting implicit manifest support, which 
will pick up on .metadata??
+                       // We ought to be able to !!
+                       block = new InsertBlock(outputBucket, new 
ClientMetadata(mimeType), persistent ? targetURI.clone() : targetURI);
+               } catch (IOException e) {
+                       fail(new InsertException(InsertException.BUCKET_ERROR, 
e, null), container, context);
+                       return;
+               }
+               
+               boolean dc = dontCompress;
+               if (!dontCompress) {
+                       dc = (archiveType == ARCHIVE_TYPE.ZIP);
+               }
+               
+               SingleFileInserter sfi = new SingleFileInserter(parent, cb, 
block, false, ctx, dc, getCHKOnly, reportMetadataOnly, token, archiveType, 
true, null, earlyEncode);
+               if(logMINOR)
+                       Logger.minor(this, "Inserting container: "+sfi+" for 
"+this);
+               cb.onTransition(this, sfi, container);
+               try {
+                       sfi.schedule(container, context);
+               } catch (InsertException e) {
+                       fail(new InsertException(InsertException.BUCKET_ERROR, 
e, null), container, context);
+                       return;
+               }
+               cb.onBlockSetFinished(this, container, context);
+       }
+
+       private void makeMetadata(ClientContext context, ObjectContainer 
container) {
+               
+               Bucket bucket = null;
+               int x = 0;
+               
+               while(true) {
+                       try {
+                               HashMap<String,Object> manifest = new 
HashMap<String,Object>();
+                               makeManifest(origMetadata, manifest, "/");
+                               Metadata md = 
Metadata.mkRedirectionManifestWithMetadata(manifest);
+                               bucket = 
context.tempBucketFactory.makeBucket(Metadata.MAX_SPLITFILE_PARAMS_LENGTH);
+                               byte[] buf = md.writeToByteArray();
+                               OutputStream os = bucket.getOutputStream();
+                               os.write(buf);
+                               os.close();
+                               containerItems.add(new ContainerElement(bucket, 
".metadata"));
+                               return;
+                       } catch (IOException e) {
+                               fail(new 
InsertException(InsertException.INTERNAL_ERROR, e, null), container, context);
+                               return;
+                       } catch (MetadataUnresolvedException e) {
+                               try {
+                                       x = resolve(e, x, bucket, null, null, 
container, context);
+                               } catch (IOException e1) {
+                                       fail(new 
InsertException(InsertException.INTERNAL_ERROR, e, null), container, context);
+                                       return;
+                               }
+                       }
+               }
+               
+       }
+
+       private int resolve(MetadataUnresolvedException e, int x, Bucket 
bucket, FreenetURI key, String element2, ObjectContainer container, 
ClientContext context) throws IOException {
+               Metadata[] m = e.mustResolve;
+               for(int i=0;i<m.length;i++) {
+                       try {
+                               byte[] buf = m[i].writeToByteArray();
+                               OutputStream os = bucket.getOutputStream();
+                               os.write(buf);
+                               os.close();
+                               String nameInArchive = ".metadata-"+(x++);
+                               containerItems.add(new ContainerElement(bucket, 
nameInArchive));
+                               Entry<String, Object> entry = 
metadataMap.get(m[i]);
+                               entry.setValue(new 
Metadata(Metadata.ARCHIVE_INTERNAL_REDIRECT, null, null, nameInArchive, null));
+                       } catch (MetadataUnresolvedException e1) {
+                               x = resolve(e, x, bucket, key, element2, 
container, context);
+                       }
+               }
+               return x;
+       }
+
+       private void fail(InsertException e, ObjectContainer container, 
ClientContext context) {
+               // Cancel all, then call the callback
+               synchronized(this) {
+                       if(finished) return;
+                       finished = true;
+               }
+               if(persistent)
+                       container.activate(cb, 1);
+               cb.onFailure(e, this, container, context);
+       }
+
+       // A persistent hashCode is helpful in debugging, and also means we can 
put
+       // these objects into sets etc when we need to.
+       
+       private final int hashCode;
+       
+       @Override
+       public int hashCode() {
+               return hashCode;
+       }
+       
+       public boolean objectCanUpdate(@SuppressWarnings("unused") 
ObjectContainer container) {
+               if(logMINOR)
+                       Logger.minor(this, "objectCanUpdate() on "+this, new 
Exception("debug"));
+               return true;
+       }
+       
+       public boolean objectCanNew(@SuppressWarnings("unused") ObjectContainer 
container) {
+               if(logMINOR)
+                       Logger.minor(this, "objectCanNew() on "+this, new 
Exception("debug"));
+               return true;
+       }
+       
+
+       
+       private String createTarBucket(Bucket outputBucket, 
@SuppressWarnings("unused") ObjectContainer container) throws IOException {
+               if(logMINOR) Logger.minor(this, "Create a TAR Bucket");
+               
+               OutputStream os = new 
BufferedOutputStream(outputBucket.getOutputStream());
+               TarOutputStream tarOS = new TarOutputStream(os);
+               tarOS.setLongFileMode(TarOutputStream.LONGFILE_GNU);
+               TarEntry ze;
+
+               for(ContainerElement ph : containerItems) {
+                       if(logMINOR)
+                               Logger.minor(this, "Putting into tar: "+ph+" 
data length "+ph.data.size()+" name "+ph.targetInArchive);
+                       ze = new TarEntry(ph.targetInArchive);
+                       ze.setModTime(0);
+                       long size = ph.data.size();
+                       ze.setSize(size);
+                       tarOS.putNextEntry(ze);
+                       BucketTools.copyTo(ph.data, tarOS, size);
+                       tarOS.closeEntry();
+               }
+
+               tarOS.closeEntry();
+               // Both finish() and close() are necessary.
+               tarOS.finish();
+               tarOS.flush();
+               tarOS.close();
+               
+               if(logMINOR)
+                       Logger.minor(this, "Archive size is 
"+outputBucket.size());
+               
+               return ARCHIVE_TYPE.TAR.mimeTypes[0];
+       }
+       
+       private String createZipBucket(Bucket outputBucket, 
@SuppressWarnings("unused") ObjectContainer container) throws IOException {
+               if(logMINOR) Logger.minor(this, "Create a ZIP Bucket");
+               
+               OutputStream os = new 
BufferedOutputStream(outputBucket.getOutputStream());
+               ZipOutputStream zos = new ZipOutputStream(os);
+               ZipEntry ze;
+
+               for(ContainerElement ph: containerItems) {
+                       ze = new ZipEntry(ph.targetInArchive);
+                       ze.setTime(0);
+                       zos.putNextEntry(ze);
+                       BucketTools.copyTo(ph.data, zos, ph.data.size());
+                       zos.closeEntry();
+               }
+
+               zos.closeEntry();
+               // Both finish() and close() are necessary.
+               zos.finish();
+               zos.flush();
+               zos.close();
+               
+               return ARCHIVE_TYPE.ZIP.mimeTypes[0];
+       }
+
+       private void makeManifest(HashMap<String, Object> manifestElements, 
HashMap<String,Object> manifest, String archivePrefix) {
+               for (Entry<String, Object>entry:manifestElements.entrySet()) {
+                       String name = entry.getKey();
+                       Object o = entry.getValue();
+                       if(o instanceof HashMap) {
+                               @SuppressWarnings("unchecked")
+                               HashMap<String,Object> hm = (HashMap<String, 
Object>) o;
+                               HashMap<String,Object> subMap = new 
HashMap<String,Object>();
+                               //System.out.println("Decompose: "+name+" 
(SubDir)");
+                               manifest.put(name, subMap);
+                               makeManifest(hm, subMap, archivePrefix+name+ 
'/');
+                               if(Logger.shouldLog(Logger.DEBUG, this))
+                                       Logger.debug(this, "Sub map for 
"+name+" : "+subMap.size()+" elements from "+hm.size());
+                       } else if (o instanceof Metadata) {
+                               //already Metadata, take as is
+                               //System.out.println("Decompose: "+name+" 
(Metadata)");
+                               manifest.put(name, o);
+                               metadataMap.put((Metadata) o, entry);
+                       } else {
+                               ManifestElement element = (ManifestElement) o;
+                               String mimeType = element.mimeOverride;
+                               ClientMetadata cm;
+                               if(mimeType == null || 
mimeType.equals(DefaultMIMETypes.DEFAULT_MIME_TYPE))
+                                       cm = null;
+                               else
+                                       cm = new ClientMetadata(mimeType);
+                               Metadata m;
+                               if(element.targetURI != null) {
+                                       //System.out.println("Decompose: 
"+name+" (ManifestElement, Redirect)");
+                                       m = new 
Metadata(Metadata.SIMPLE_REDIRECT, null, null, element.targetURI, cm);
+                               } else {
+                                       //System.out.println("Decompose: 
"+name+" (ManifestElement, Data)");
+                                       containerItems.add(new 
ContainerElement(element.data, archivePrefix+name));
+                                       m = new 
Metadata(Metadata.ARCHIVE_INTERNAL_REDIRECT, null, null, 
archivePrefix+element.fullName, cm);
+                               }
+                               manifest.put(name, m);
+                               metadataMap.put(m, entry);
+                       }
+               }
+       }
+}


Property changes on: 
trunk/freenet/src/freenet/client/async/ContainerInserter.java
___________________________________________________________________
Added: svn:mime-type
   + text/plain

_______________________________________________
cvs mailing list
[email protected]
http://emu.freenetproject.org/cgi-bin/mailman/listinfo/cvs

Reply via email to