/* MediaserverFiler.java * * Copyright (c) 2002, Benja Fallenstein * * This file is part of Gzz. * * Gzz is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Gzz is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General * Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with Gzz; if not, write to the Free * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA * * */ /* * Written by Benja Fallenstein */ package gzz.mediaserver; import gzz.media.MediaInputStream; import gzz.media.MediaOutputStream; import gzz.util.CopyUtil; import gzz.util.Filer; import gzz.util.HeaderUtil; import gzz.util.Version; import gzz.util.VersionFormat; import java.io.*; import java.util.*; public class MediaserverFiler implements Filer { public static boolean dbg = false; private static void pa(String s) { System.out.println(s); } public static final String versionContentType = "application/x-GZZ2-version"; public static final String diffContentType = "application/x-GZZ2-msfiler-diff"; public static class Group extends gzz.util.Filer.AbstractGroup { public Mediaserver ms; Version emptyVersion; VersionFormat fmt; public Group(Version emptyVersion, Mediaserver ms, VersionFormat fmt) { this.ms = ms; this.emptyVersion = emptyVersion; this.fmt = fmt; } protected Filer createFiler(String id) throws IOException { return new MediaserverFiler(ms, id, this.emptyVersion, fmt); } } Mediaserver ms; String pointer; Version emptyVersion; VersionFormat fmt; Mediaserver.Id current; public MediaserverFiler(Mediaserver ms, String pointer, Version emptyVersion, VersionFormat fmt) throws IOException { this.ms = ms; this.pointer = pointer; this.emptyVersion = emptyVersion; this.fmt = fmt; current = ms.getPointer(pointer); } public Version load() throws IOException { return load(current, null); } protected Version load(Mediaserver.Id id, String[] hdr) throws IOException { if(id == null) return emptyVersion; MediaserverBlock block = null; try { block = ms.getDatum(id); } catch(IOException e) {} if(block != null) { if(hdr != null) hdr[0] = block.getRawHeader(); return fmt.readVersion(block.getInputStream()); } Set s = ms.getDiffsTo(id); if(s.isEmpty()) throw new IOException("No diff to version: "+id); for(Iterator i = s.iterator(); i.hasNext();) { Mediaserver.Id diffId = (Mediaserver.Id)i.next(); try { MediaserverBlock diffBlock = ms.getDatum(diffId); if(!diffBlock.getContentType().equals(diffContentType)) throw new IOException("Unknown diff content type: "+ diffBlock.getContentType()); Version from; if(diffBlock.getHeader().containsKey("x-gzz-diff-from")) { Mediaserver.Id fromId = new Mediaserver.Id( HeaderUtil.getOne(diffBlock.getHeader(), "X-Gzz-Diff-From")); from = load(fromId, null); } else { from = emptyVersion; } InputStream is = diffBlock.getInputStream(); String headerFrom = HeaderUtil.readRaw(is); String headerTo = HeaderUtil.readRaw(is); Version.Diff diff = fmt.readDiff(is); Version v = diff.applyTo(from); if(!id.equals(getVersionId(v, headerTo))) { if(true) { pa("============= DIFF ==============="); pa(new String(CopyUtil.readBytes(diffBlock.getInputStream()))); pa("=================================="); pa("Diff: "+diff); pa(""); pa("From: "+from); pa(""); pa("Gives: "+v); pa("============== BAD ==============="); ByteArrayOutputStream bos = new ByteArrayOutputStream(); fmt.writeVersion(bos, v); pa(new String(bos.toByteArray())); pa("=================================="); } throw new IOException("Version doesn't match: "+v+"\n"+ "Wanted id = "+id+"\n"+ "Regenerated id = "+getVersionId(v, headerTo)+"\n"+ "Using diff: "+diffId+"\n"); } if(hdr != null) hdr[0] = headerTo; return v; } catch(IOException e) { System.out.println("IOException when traversing diff path: "+e); e.printStackTrace(); System.out.println("Backtracking."); } } throw new IOException("Didn't find valid diff path to version: "+id); } protected Mediaserver.Id getVersionId(Version v, String header) throws IOException { ByteArrayOutputStream bos = new ByteArrayOutputStream(); bos.write(header.getBytes("US-ASCII")); fmt.writeVersion(bos, v); return IDSpace.instance.createID(bos.toByteArray()); } public void save(Version v) throws IOException { String[] arr = new String[1]; Version old = load(current, arr); if(v.equals(old)) return; Mediaserver.Id diffFrom = current; String headerFrom; if(current != null) headerFrom = arr[0]; else headerFrom = "" + ((char)0xd) + ((char)0xa); // just CRLF ByteArrayOutputStream bos = new ByteArrayOutputStream(); fmt.writeVersion(bos, v); byte[] currentBytes = bos.toByteArray(); Mediaserver.Id diffTo = ms.addDatum(currentBytes, versionContentType, current); String headerTo = ms.getDatum(diffTo).getRawHeader(); if(!diffTo.equals(getVersionId(v, headerTo))) throw new Error("BAD INTERNAL ERROR: Block id recreation failed"); Mediaserver.Id was_current = current; current = diffTo; ms.setPointer(pointer, current); // now, create diff Version.Diff diff = v.getDiffFrom(old); if(!diff.applyTo(old).equals(v)) throw new Error("Diff is wrong (saved whole version instead)"); bos = new ByteArrayOutputStream(); bos.write(headerFrom.getBytes("US-ASCII")); bos.write(headerTo.getBytes("US-ASCII")); fmt.writeDiff(bos, diff); List h = new ArrayList(3); h.add("Content-Type: " + diffContentType); h.add("X-Gzz-Diff-To: " + diffTo.getString()); if(diffFrom != null) h.add("X-Gzz-Diff-From: " + diffFrom.getString()); ms.addDatum(bos.toByteArray(), h, diffTo, true); // having added the diff, we can remove the version ms.expungeDatum(current); // try whether we can recreate the version try { Version w = load(current, arr); if(!v.equals(w)) throw new Error("Versions didn't match"); } catch(Throwable t) { // re-add bos = new ByteArrayOutputStream(); fmt.writeVersion(bos, v); byte[] barr = bos.toByteArray(); boolean equal = true; if(barr.length == currentBytes.length) for(int i=0; i