You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@zookeeper.apache.org by ph...@apache.org on 2010/06/09 17:26:25 UTC

svn commit: r953041 [2/6] - in /hadoop/zookeeper/trunk: ./ src/contrib/loggraph/ src/contrib/loggraph/bin/ src/contrib/loggraph/src/ src/contrib/loggraph/src/java/ src/contrib/loggraph/src/java/org/ src/contrib/loggraph/src/java/org/apache/ src/contrib...

Added: hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/TxnLogSource.java
URL: http://svn.apache.org/viewvc/hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/TxnLogSource.java?rev=953041&view=auto
==============================================================================
--- hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/TxnLogSource.java (added)
+++ hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/TxnLogSource.java Wed Jun  9 15:26:22 2010
@@ -0,0 +1,377 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.zookeeper.graph;
+
+import java.io.ByteArrayInputStream;
+import java.io.EOFException;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.text.DateFormat;
+import java.util.Date;
+import java.util.zip.Adler32;
+import java.util.zip.Checksum;
+import java.util.HashMap;
+
+import org.apache.jute.BinaryInputArchive;
+import org.apache.jute.InputArchive;
+import org.apache.jute.Record;
+import org.apache.zookeeper.server.TraceFormatter;
+import org.apache.zookeeper.server.persistence.FileHeader;
+import org.apache.zookeeper.server.persistence.FileTxnLog;
+import org.apache.zookeeper.server.util.SerializeUtils;
+import org.apache.zookeeper.txn.TxnHeader;
+
+import org.apache.zookeeper.ZooDefs.OpCode;
+
+import org.apache.zookeeper.txn.CreateSessionTxn;
+import org.apache.zookeeper.txn.CreateTxn;
+import org.apache.zookeeper.txn.DeleteTxn;
+import org.apache.zookeeper.txn.ErrorTxn;
+import org.apache.zookeeper.txn.SetACLTxn;
+import org.apache.zookeeper.txn.SetDataTxn;
+import org.apache.zookeeper.txn.TxnHeader;
+
+import java.io.File;
+import java.io.Closeable;
+import java.io.FileNotFoundException;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+import org.apache.log4j.Logger;
+
+public class TxnLogSource implements LogSource {
+    private static final Logger LOG = Logger.getLogger(TxnLogSource.class);
+
+    private LogSkipList skiplist = null;
+    private static final int skipN = 10000;
+
+    private String file = null;
+    private long starttime = 0;
+    private long endtime = 0;
+    private long size = 0;
+
+    public boolean overlapsRange(long starttime, long endtime) {
+	return (starttime <= this.endtime && endtime >= this.starttime);
+    }
+
+    public long size() { return size; }
+    public long getStartTime() { return starttime; }
+    public long getEndTime() { return endtime; }
+    public LogSkipList getSkipList() { return skiplist; }
+
+    public static boolean isTransactionFile(String file) throws IOException {
+        RandomAccessFileReader reader = new RandomAccessFileReader(new File(file));
+        BinaryInputArchive logStream = new BinaryInputArchive(reader);
+        FileHeader fhdr = new FileHeader();
+        fhdr.deserialize(logStream, "fileheader");
+	reader.close();
+
+        return fhdr.getMagic() == FileTxnLog.TXNLOG_MAGIC;
+    }
+
+    private class TxnLogSourceIterator implements LogIterator {
+	private LogEntry next = null;
+	private long starttime = 0;
+	private long endtime = 0;
+	private TxnLogSource src = null;
+	private RandomAccessFileReader reader = null;
+	private BinaryInputArchive logStream = null;
+	private long skippedAtStart = 0;
+	private FilterOp filter = null;
+
+	public TxnLogSourceIterator(TxnLogSource src, long starttime, long endtime) throws IllegalArgumentException, FilterException {
+	    this(src,starttime,endtime,null);
+	}
+	
+	public TxnLogSourceIterator(TxnLogSource src, long starttime, long endtime, FilterOp filter) throws IllegalArgumentException, FilterException {
+	    try {
+		this.src = src;
+		this.starttime = starttime;
+		this.endtime = endtime;
+		reader = new RandomAccessFileReader(new File(src.file));
+		logStream = new BinaryInputArchive(reader);
+		FileHeader fhdr = new FileHeader();
+		fhdr.deserialize(logStream, "fileheader");
+	    } catch (Exception e) {
+		throw new IllegalArgumentException("Cannot open transaction log ("+src.file+") :" + e);
+	    }
+	    
+	    LogSkipList.Mark start = src.getSkipList().findMarkBefore(starttime);
+	    try {
+		reader.seek(start.getBytes());
+		skippedAtStart = start.getEntriesSkipped();
+	    } catch (IOException ioe) {
+		// if we can't skip, we should just read from the start
+	    }
+
+	    this.filter = filter;
+
+	    LogEntry e;
+	    while ((e = readNextEntry()) != null && e.getTimestamp() < endtime) {
+		if (e.getTimestamp() >= starttime && (filter == null || filter.matches(e))  ) {
+		    next = e;
+		    return;
+		}
+		skippedAtStart++;
+	    }
+
+
+	}
+	
+	public long size() throws IOException {
+	    if (this.endtime >= src.getEndTime()) {
+		return src.size() - skippedAtStart;
+	    }
+	    
+	    long pos = reader.getPosition();
+	    LogEntry e;
+
+	    LogSkipList.Mark lastseg = src.getSkipList().findMarkBefore(this.endtime);
+	    reader.seek(lastseg.getBytes());
+	    // number of entries skipped to get to the end of the iterator, less the number skipped to get to the start
+	    long count = lastseg.getEntriesSkipped() - skippedAtStart; 
+
+	    while ((e = readNextEntry()) != null) {
+		if (e.getTimestamp() > this.endtime) {
+		    break;
+		}
+		count++;
+	    }
+	    reader.seek(pos);;
+
+	    return count;
+	}
+	
+	private LogEntry readNextEntry() {
+	    LogEntry e = null;
+	    try {
+		long crcValue;
+		byte[] bytes;
+		try {
+		    crcValue = logStream.readLong("crcvalue");
+		    
+		    bytes = logStream.readBuffer("txnEntry");
+		} catch (EOFException ex) {
+		    return null;
+		}
+		
+		if (bytes.length == 0) {
+		    return null;
+		}
+		Checksum crc = new Adler32();
+		crc.update(bytes, 0, bytes.length);
+		if (crcValue != crc.getValue()) {
+		    throw new IOException("CRC doesn't match " + crcValue +
+					  " vs " + crc.getValue());
+		}
+		InputArchive iab = BinaryInputArchive.getArchive(new ByteArrayInputStream(bytes));
+		TxnHeader hdr = new TxnHeader();
+		Record r = SerializeUtils.deserializeTxn(iab, hdr);
+
+		switch (hdr.getType()) {
+		case OpCode.createSession: {
+		    e = new TransactionEntry(hdr.getTime(), hdr.getClientId(), hdr.getCxid(), hdr.getZxid(), "createSession");
+		}
+		    break;
+		case OpCode.closeSession: {
+		    e = new TransactionEntry(hdr.getTime(), hdr.getClientId(), hdr.getCxid(), hdr.getZxid(), "closeSession");
+		}
+		    break;
+		case OpCode.create:
+		    if (r != null) {
+			CreateTxn create = (CreateTxn)r;
+			String path = create.getPath();
+			e = new TransactionEntry(hdr.getTime(), hdr.getClientId(), hdr.getCxid(), hdr.getZxid(), "create", path);
+		    }
+		    break;
+		case OpCode.setData:
+		    if (r != null) {
+			SetDataTxn set = (SetDataTxn)r;
+			String path = set.getPath();
+			e = new TransactionEntry(hdr.getTime(), hdr.getClientId(), hdr.getCxid(), hdr.getZxid(), "setData", path);
+		    }
+		    break;
+		case OpCode.setACL:
+		    if (r != null) {
+			SetACLTxn setacl = (SetACLTxn)r;
+			String path = setacl.getPath();
+		    e = new TransactionEntry(hdr.getTime(), hdr.getClientId(), hdr.getCxid(), hdr.getZxid(), "setACL", path);
+		    }
+		    break;
+		case OpCode.error:
+		    if (r != null)  {
+			ErrorTxn error = (ErrorTxn)r;
+			
+			e = new TransactionEntry(hdr.getTime(), hdr.getClientId(), hdr.getCxid(), hdr.getZxid(), "error", "Error: " + error.getErr());
+		    }
+		    break;
+		default:
+		    LOG.info("Unknown op: " + hdr.getType());
+		    break;
+		}
+		
+		if (logStream.readByte("EOR") != 'B') {
+		    throw new EOFException("Last transaction was partial.");
+		}
+	    } catch (Exception ex) {
+		LOG.error("Error reading transaction from (" + src.file + ") :" + e);
+		return null;
+	    }
+	    return e;
+	}
+
+	public boolean hasNext() {
+	    return next != null;
+	}
+	
+	public LogEntry next() throws NoSuchElementException {
+	    LogEntry ret = next;
+	    LogEntry e = readNextEntry();
+
+	    if (filter != null) {
+		try {
+		    while (e != null && !filter.matches(e)) {
+			e = readNextEntry();
+		    }
+		} catch (FilterException fe) {
+		    throw new NoSuchElementException(fe.toString());
+		}
+	    }
+	    if (e != null && e.getTimestamp() < endtime) {
+		next = e;
+	    } else {
+		next = null;
+	    }
+	    return ret;
+	}
+
+	public void remove() throws UnsupportedOperationException {
+	    throw new UnsupportedOperationException("remove not supported for Txn logs");
+	}
+	
+	public void close() throws IOException {
+	    reader.close();
+	}
+    }
+
+    public LogIterator iterator(long starttime, long endtime) throws IllegalArgumentException {
+	try {
+	    return iterator(starttime, endtime, null);
+	} catch (FilterException fe) {
+	    assert(false); // should never ever happen
+	    return null;
+	}
+    }
+
+    public LogIterator iterator(long starttime, long endtime, FilterOp filter) throws IllegalArgumentException, FilterException {
+	// sanitise start and end times
+	if (endtime < starttime) {
+	    throw new IllegalArgumentException("End time (" +  endtime + ") must be greater or equal to starttime (" + starttime + ")");
+	}
+
+	return new TxnLogSourceIterator(this, starttime, endtime, filter);
+    }
+
+    public LogIterator iterator() throws IllegalArgumentException {
+	return iterator(starttime, endtime+1);
+    }
+    
+    public TxnLogSource(String file) throws IOException {
+	this.file = file;
+
+	skiplist = new LogSkipList();
+
+	RandomAccessFileReader reader = new RandomAccessFileReader(new File(file));
+	try {
+	    BinaryInputArchive logStream = new BinaryInputArchive(reader);
+	    FileHeader fhdr = new FileHeader();
+	    fhdr.deserialize(logStream, "fileheader");
+	    
+	    byte[] bytes = null;
+	    while (true) {
+		long lastFp = reader.getPosition();
+
+		long crcValue;
+
+		try {
+		    crcValue = logStream.readLong("crcvalue");
+		    bytes = logStream.readBuffer("txnEntry");
+		} catch (EOFException e) {
+		    break;
+		}
+		
+		if (bytes.length == 0) {
+		    break;
+		}
+		Checksum crc = new Adler32();
+		crc.update(bytes, 0, bytes.length);
+		if (crcValue != crc.getValue()) {
+		    throw new IOException("CRC doesn't match " + crcValue +
+					  " vs " + crc.getValue());
+		}
+		if (logStream.readByte("EOR") != 'B') {
+		    throw new EOFException("Last transaction was partial.");
+		}
+		InputArchive iab = BinaryInputArchive.getArchive(new ByteArrayInputStream(bytes));
+		TxnHeader hdr = new TxnHeader();
+		Record r = SerializeUtils.deserializeTxn(iab, hdr);
+		
+		if (starttime == 0) {
+		    starttime = hdr.getTime();
+		}
+		endtime = hdr.getTime();
+
+		if (size % skipN == 0) {
+		    skiplist.addMark(hdr.getTime(), lastFp, size);
+		}
+		size++;
+	    }
+	    if (bytes == null) {
+		throw new IOException("Nothing read from ("+file+")");
+	    }
+	} finally {
+	    reader.close();
+	}
+    }
+
+    public String toString() {
+	return "TxnLogSource(file=" + file + ", size=" + size + ", start=" + starttime + ", end=" + endtime +")";
+    }
+
+    public static void main(String[] args) throws IOException, FilterException {
+	TxnLogSource s = new TxnLogSource(args[0]);
+	System.out.println(s);
+
+	LogIterator iter;
+
+	if (args.length == 3) {
+	    long starttime = Long.valueOf(args[1]);
+	    long endtime = Long.valueOf(args[2]);
+	    FilterOp fo = new FilterParser("(or (and (> zxid 0x2f0bd6f5e0) (< zxid 0x2f0bd6f5e9)) (= operation \"error\"))").parse();
+	    System.out.println("fo: " + fo);
+	    iter = s.iterator(starttime, endtime, fo);
+	} else {
+	    iter = s.iterator();
+	}
+	System.out.println(iter);
+	while (iter.hasNext()) {
+	    	    System.out.println(iter.next());
+	}
+	iter.close();
+    }
+}

Added: hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/AndOp.java
URL: http://svn.apache.org/viewvc/hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/AndOp.java?rev=953041&view=auto
==============================================================================
--- hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/AndOp.java (added)
+++ hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/AndOp.java Wed Jun  9 15:26:22 2010
@@ -0,0 +1,33 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.zookeeper.graph.filterops;
+
+import org.apache.zookeeper.graph.LogEntry;
+import org.apache.zookeeper.graph.FilterOp;
+import org.apache.zookeeper.graph.FilterException;
+
+public class AndOp extends FilterOp {
+    public boolean matches(LogEntry entry) throws FilterException {
+	for (FilterOp f : subOps) {
+	    if (!f.matches(entry)) {
+		return false;
+	    }
+	}
+	return true;
+    }
+}

Added: hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/Arg.java
URL: http://svn.apache.org/viewvc/hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/Arg.java?rev=953041&view=auto
==============================================================================
--- hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/Arg.java (added)
+++ hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/Arg.java Wed Jun  9 15:26:22 2010
@@ -0,0 +1,36 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.zookeeper.graph.filterops;
+
+import org.apache.zookeeper.graph.FilterOp.*;
+
+public class Arg<T> {
+    private ArgType type;
+    protected T value;
+    
+    protected Arg(ArgType type) {
+	this.type = type;
+    }
+    
+    public ArgType getType() { return type; }
+    public T getValue() { return value; }
+
+    public String toString() {
+	return "[" + type + ":" + value + "]";
+    }
+}

Added: hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/EqualsOp.java
URL: http://svn.apache.org/viewvc/hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/EqualsOp.java?rev=953041&view=auto
==============================================================================
--- hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/EqualsOp.java (added)
+++ hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/EqualsOp.java Wed Jun  9 15:26:22 2010
@@ -0,0 +1,44 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.zookeeper.graph.filterops;
+
+import org.apache.zookeeper.graph.LogEntry;
+import org.apache.zookeeper.graph.FilterOp;
+import org.apache.zookeeper.graph.FilterException;
+
+public class EqualsOp extends FilterOp {
+    public boolean matches(LogEntry entry) throws FilterException {
+
+	Object last = null;
+	for (Arg a : args) {
+	    Object v = a.getValue();
+	    if (a.getType() == FilterOp.ArgType.SYMBOL) {
+		String key = (String)a.getValue();
+		v = entry.getAttribute(key);
+	    }
+
+	    if (last != null
+		&& !last.equals(v)) {
+		return false;
+	    }
+	    last = v;
+	}
+
+	return true;
+    }
+}    

Added: hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/GreaterThanOp.java
URL: http://svn.apache.org/viewvc/hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/GreaterThanOp.java?rev=953041&view=auto
==============================================================================
--- hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/GreaterThanOp.java (added)
+++ hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/GreaterThanOp.java Wed Jun  9 15:26:22 2010
@@ -0,0 +1,70 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.zookeeper.graph.filterops;
+
+import org.apache.zookeeper.graph.LogEntry;
+import org.apache.zookeeper.graph.FilterOp;
+import org.apache.zookeeper.graph.FilterException;
+
+public class GreaterThanOp extends FilterOp {
+    public boolean matches(LogEntry entry) throws FilterException {
+	Arg first = args.get(0);
+	
+	if (first != null) {
+	    FilterOp.ArgType type = first.getType();
+	    if (type == FilterOp.ArgType.SYMBOL) {
+		String key = (String)first.getValue();
+		Object v = entry.getAttribute(key);
+		if (v instanceof String) {
+		    type = FilterOp.ArgType.STRING;
+		} else if (v instanceof Double || v instanceof Long || v instanceof Integer || v instanceof Short) {
+		    type = FilterOp.ArgType.NUMBER;
+		} else {
+		    throw new FilterException("LessThanOp: Invalid argument, first argument resolves to neither a String nor a Number");
+		}
+	    }
+	    
+	    Object last = null;
+	    for (Arg a : args) {
+		Object v = a.getValue();
+		if (a.getType() == FilterOp.ArgType.SYMBOL) {
+		    String key = (String)a.getValue();
+		    v = entry.getAttribute(key);
+		}
+
+		if (last != null) {
+		    if (type == FilterOp.ArgType.STRING) {
+			if (((String)last).compareTo((String)v) <= 0) {
+			    return false;
+			}
+		    } else if (type == FilterOp.ArgType.NUMBER) {
+			//			System.out.println("last[" + ((Number)last).longValue() + "] v["+ ((Number)v).longValue() + "]");
+			if (((Number)last).longValue() <= ((Number)v).longValue()) {
+			    return false;
+			}
+		    }
+		}
+		last = v;
+	    }
+	    return true;
+	} else { 
+	    return true; 
+	}
+    }
+	
+}    

Added: hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/LessThanOp.java
URL: http://svn.apache.org/viewvc/hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/LessThanOp.java?rev=953041&view=auto
==============================================================================
--- hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/LessThanOp.java (added)
+++ hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/LessThanOp.java Wed Jun  9 15:26:22 2010
@@ -0,0 +1,69 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.zookeeper.graph.filterops;
+
+import org.apache.zookeeper.graph.LogEntry;
+import org.apache.zookeeper.graph.FilterOp;
+import org.apache.zookeeper.graph.FilterException;
+
+public class LessThanOp extends FilterOp {
+    public boolean matches(LogEntry entry) throws FilterException {
+	Arg first = args.get(0);
+	
+	if (first != null) {
+	    FilterOp.ArgType type = first.getType();
+	    if (type == FilterOp.ArgType.SYMBOL) {
+		String key = (String)first.getValue();
+		Object v = entry.getAttribute(key);
+		if (v instanceof String) {
+		    type = FilterOp.ArgType.STRING;
+		} else if (v instanceof Double || v instanceof Long || v instanceof Integer || v instanceof Short) {
+		    type = FilterOp.ArgType.NUMBER;
+		} else {
+		    throw new FilterException("LessThanOp: Invalid argument, first argument resolves to neither a String nor a Number");
+		}
+	    }
+	    
+	    Object last = null;
+	    for (Arg a : args) {
+		Object v = a.getValue();
+		if (a.getType() == FilterOp.ArgType.SYMBOL) {
+		    String key = (String)a.getValue();
+		    v = entry.getAttribute(key);
+		}
+
+		if (last != null) {
+		    if (type == FilterOp.ArgType.STRING) {
+			if (((String)last).compareTo((String)v) >= 0) {
+			    return false;
+			}
+		    } else if (type == FilterOp.ArgType.NUMBER) {
+			if (((Number)last).doubleValue() >= ((Number)v).doubleValue()) {
+			    return false;
+			}
+		    }
+		}
+		last = v;
+	    }
+	    return true;
+	} else { 
+	    return true; 
+	}
+    }
+	
+}    

Added: hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/NotOp.java
URL: http://svn.apache.org/viewvc/hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/NotOp.java?rev=953041&view=auto
==============================================================================
--- hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/NotOp.java (added)
+++ hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/NotOp.java Wed Jun  9 15:26:22 2010
@@ -0,0 +1,31 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.zookeeper.graph.filterops;
+
+import org.apache.zookeeper.graph.LogEntry;
+import org.apache.zookeeper.graph.FilterOp;
+import org.apache.zookeeper.graph.FilterException;
+
+public class NotOp extends FilterOp {
+    public boolean matches(LogEntry entry) throws FilterException {
+	if (subOps.size() != 1) {
+	    throw new FilterException("Not operation can only take one argument");
+	}
+	return !subOps.get(0).matches(entry);
+    }
+}

Added: hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/NumberArg.java
URL: http://svn.apache.org/viewvc/hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/NumberArg.java?rev=953041&view=auto
==============================================================================
--- hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/NumberArg.java (added)
+++ hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/NumberArg.java Wed Jun  9 15:26:22 2010
@@ -0,0 +1,28 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.zookeeper.graph.filterops;
+
+import org.apache.zookeeper.graph.FilterOp.*;
+
+public class NumberArg extends Arg<Long> {
+    public NumberArg(Long value) {
+	super(ArgType.NUMBER);
+	this.value = value;
+    }
+};
+

Added: hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/OrOp.java
URL: http://svn.apache.org/viewvc/hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/OrOp.java?rev=953041&view=auto
==============================================================================
--- hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/OrOp.java (added)
+++ hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/OrOp.java Wed Jun  9 15:26:22 2010
@@ -0,0 +1,33 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.zookeeper.graph.filterops;
+
+import org.apache.zookeeper.graph.LogEntry;
+import org.apache.zookeeper.graph.FilterOp;
+import org.apache.zookeeper.graph.FilterException;
+
+public class OrOp extends FilterOp {
+    public boolean matches(LogEntry entry) throws FilterException {
+	for (FilterOp f : subOps) {
+	    if (f.matches(entry)) {
+		return true;
+	    }
+	}
+	return false;
+    }
+}    

Added: hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/StringArg.java
URL: http://svn.apache.org/viewvc/hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/StringArg.java?rev=953041&view=auto
==============================================================================
--- hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/StringArg.java (added)
+++ hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/StringArg.java Wed Jun  9 15:26:22 2010
@@ -0,0 +1,28 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.zookeeper.graph.filterops;
+
+import org.apache.zookeeper.graph.FilterOp.*;
+
+public class StringArg extends Arg<String> {
+    public StringArg(String value) {
+	super(ArgType.STRING);
+	    this.value = value;
+    }
+};
+

Added: hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/SymbolArg.java
URL: http://svn.apache.org/viewvc/hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/SymbolArg.java?rev=953041&view=auto
==============================================================================
--- hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/SymbolArg.java (added)
+++ hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/SymbolArg.java Wed Jun  9 15:26:22 2010
@@ -0,0 +1,27 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.zookeeper.graph.filterops;
+
+import org.apache.zookeeper.graph.FilterOp.*;
+
+public class SymbolArg extends Arg<String> {
+    public SymbolArg(String value) {
+	super(ArgType.SYMBOL);
+	this.value = value;
+    }
+};

Added: hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/XorOp.java
URL: http://svn.apache.org/viewvc/hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/XorOp.java?rev=953041&view=auto
==============================================================================
--- hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/XorOp.java (added)
+++ hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/filterops/XorOp.java Wed Jun  9 15:26:22 2010
@@ -0,0 +1,40 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.zookeeper.graph.filterops;
+
+import org.apache.zookeeper.graph.LogEntry;
+import org.apache.zookeeper.graph.FilterOp;
+import org.apache.zookeeper.graph.FilterException;
+
+public class XorOp extends FilterOp {
+    public boolean matches(LogEntry entry) throws FilterException {
+	int count = 0;
+	for (FilterOp f : subOps) {
+	    if (f.matches(entry)) {
+		count++;
+		if (count > 1) {
+		    return false;
+		}
+	    }
+	}
+	if (count == 1) {
+	    return true;
+	}
+	return false;
+    }
+}    

Added: hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/servlets/FileLoader.java
URL: http://svn.apache.org/viewvc/hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/servlets/FileLoader.java?rev=953041&view=auto
==============================================================================
--- hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/servlets/FileLoader.java (added)
+++ hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/servlets/FileLoader.java Wed Jun  9 15:26:22 2010
@@ -0,0 +1,60 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.zookeeper.graph.servlets;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.FileNotFoundException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.json.simple.JSONArray;
+import org.json.simple.JSONObject;
+import org.json.simple.JSONValue;
+
+import org.apache.zookeeper.graph.*;
+
+public class FileLoader extends JsonServlet
+{
+    private MergedLogSource source = null;
+    
+    public FileLoader(MergedLogSource src) throws Exception {
+	source = src;
+    }
+
+    String handleRequest(JsonRequest request) throws Exception
+    {
+	String output = "";
+		
+	String file = request.getString("path", "/");
+	JSONObject o = new JSONObject();
+	try {
+	    this.source.addSource(file);
+	    o.put("status", "OK");
+	
+	} catch (Exception e) {
+	    o.put("status", "ERR");
+	    o.put("error",  e.toString());
+	}
+	
+	return JSONValue.toJSONString(o);
+    }
+}
\ No newline at end of file

Added: hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/servlets/Fs.java
URL: http://svn.apache.org/viewvc/hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/servlets/Fs.java?rev=953041&view=auto
==============================================================================
--- hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/servlets/Fs.java (added)
+++ hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/servlets/Fs.java Wed Jun  9 15:26:22 2010
@@ -0,0 +1,69 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.zookeeper.graph.servlets;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.FileNotFoundException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.json.simple.JSONArray;
+import org.json.simple.JSONObject;
+import org.json.simple.JSONValue;
+import java.util.Arrays;
+import java.util.Comparator;
+
+public class Fs extends JsonServlet
+{
+    String handleRequest(JsonRequest request) throws Exception
+    {
+	String output = "";
+	JSONArray filelist = new JSONArray();
+
+	File base = new File(request.getString("path", "/"));
+	if (!base.exists() || !base.isDirectory()) {
+	    throw new FileNotFoundException("Couldn't find [" + request + "]");
+	}
+	File[] files = base.listFiles();
+	Arrays.sort(files, new Comparator<File>() { 
+		public int compare(File o1, File o2) {
+		    if (o1.isDirectory() != o2.isDirectory()) {
+			if (o1.isDirectory()) {
+			    return -1;
+			} else {
+			    return 1;
+			}
+		    }
+		    return o1.getName().compareToIgnoreCase(o2.getName());
+		} 
+	    });
+	
+	for (File f : files) {
+	    JSONObject o = new JSONObject();
+	    o.put("file", f.getName());
+	    o.put("type", f.isDirectory() ? "D" : "F");
+	    o.put("path", f.getCanonicalPath());
+	    filelist.add(o);
+	}
+	return JSONValue.toJSONString(filelist);
+    }
+}
\ No newline at end of file

Added: hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/servlets/GraphData.java
URL: http://svn.apache.org/viewvc/hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/servlets/GraphData.java?rev=953041&view=auto
==============================================================================
--- hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/servlets/GraphData.java (added)
+++ hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/servlets/GraphData.java Wed Jun  9 15:26:22 2010
@@ -0,0 +1,84 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.zookeeper.graph.servlets;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.FileNotFoundException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+
+import org.json.simple.JSONArray;
+import org.json.simple.JSONObject;
+import org.json.simple.JSONValue;
+
+import org.apache.zookeeper.graph.*;
+import org.apache.log4j.Logger;
+
+public class GraphData extends JsonServlet
+{
+    private static final Logger LOG = Logger.getLogger(GraphData.class);
+    private static final int DEFAULT_PERIOD = 1000;
+
+    private LogSource source = null;
+
+    public GraphData(LogSource src) throws Exception {
+	this.source = src; 
+    }
+
+    String handleRequest(JsonRequest request) throws Exception {
+	
+
+	long starttime = 0;
+	long endtime = 0;
+	long period = 0;
+	FilterOp fo = null;
+
+	starttime = request.getNumber("start", 0);
+	endtime = request.getNumber("end", 0);
+	period = request.getNumber("period", 0);
+	String filterstr = request.getString("filter", "");
+
+	if (filterstr.length() > 0) {
+	    fo = new FilterParser(filterstr).parse();
+	}
+	
+	if (starttime == 0) { starttime = source.getStartTime(); }
+	if (endtime == 0) { 
+	    if (period > 0) {
+		endtime = starttime + period;
+	    } else {
+		endtime = starttime + DEFAULT_PERIOD; 
+	    }
+	}
+
+	if (LOG.isDebugEnabled()) {
+	    LOG.debug("handle(start= " + starttime + ", end=" + endtime + ", period=" + period + ")");
+	}
+	
+	LogIterator iterator = (fo != null) ? 
+	    source.iterator(starttime, endtime, fo) : source.iterator(starttime, endtime);
+	return new JsonGenerator(iterator).toString();
+    }
+}
\ No newline at end of file

Added: hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/servlets/JsonServlet.java
URL: http://svn.apache.org/viewvc/hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/servlets/JsonServlet.java?rev=953041&view=auto
==============================================================================
--- hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/servlets/JsonServlet.java (added)
+++ hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/servlets/JsonServlet.java Wed Jun  9 15:26:22 2010
@@ -0,0 +1,85 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.zookeeper.graph.servlets;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.json.simple.JSONObject;
+import org.json.simple.JSONValue;
+
+import java.util.Map;
+
+abstract public class JsonServlet extends HttpServlet {
+    abstract String handleRequest(JsonRequest request) throws Exception;
+
+    protected class JsonRequest {
+	private Map map;
+
+	public JsonRequest(ServletRequest request) {
+	    map = request.getParameterMap();
+	}
+	
+	public long getNumber(String name, long defaultnum) {
+	    String[] vals = (String[])map.get(name);
+	    if (vals == null || vals.length == 0) {
+		return defaultnum;
+	    }
+
+	    try {
+		return Long.valueOf(vals[0]);
+	    } catch (NumberFormatException e) {
+		return defaultnum;
+	    }
+	}
+	
+	public String getString(String name, String defaultstr) {
+	    String[] vals = (String[])map.get(name);
+	    if (vals == null || vals.length == 0) {
+		return defaultstr;
+	    } else {
+		return vals[0];
+	    }
+	}
+    }
+
+    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+    {
+        response.setContentType("text/plain;charset=utf-8");
+        response.setStatus(HttpServletResponse.SC_OK);
+	
+	try {
+	    String req = request.getRequestURI().substring(request.getServletPath().length());
+
+	    response.getWriter().println(handleRequest(new JsonRequest(request)));
+	} catch (Exception e) {
+	    JSONObject o = new JSONObject();
+	    o.put("error", e.toString());
+	    response.getWriter().println(JSONValue.toJSONString(o));
+	} catch (java.lang.OutOfMemoryError oom) {
+	    JSONObject o = new JSONObject();
+	    o.put("error", "Out of memory. Perhaps you've requested too many logs. Try narrowing you're filter criteria.");
+	    response.getWriter().println(JSONValue.toJSONString(o));
+	}
+    }
+}

Added: hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/servlets/NumEvents.java
URL: http://svn.apache.org/viewvc/hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/servlets/NumEvents.java?rev=953041&view=auto
==============================================================================
--- hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/servlets/NumEvents.java (added)
+++ hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/servlets/NumEvents.java Wed Jun  9 15:26:22 2010
@@ -0,0 +1,86 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.zookeeper.graph.servlets;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.FileNotFoundException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.json.simple.JSONArray;
+import org.json.simple.JSONObject;
+import org.json.simple.JSONValue;
+
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+
+import org.apache.zookeeper.graph.*;
+import org.apache.log4j.Logger;
+
+
+public class NumEvents extends JsonServlet
+{
+    private static final Logger LOG = Logger.getLogger(NumEvents.class);
+    private static final int DEFAULT_PERIOD = 1000;
+
+    private LogSource source = null;
+
+    public NumEvents(LogSource src) throws Exception {
+	this.source = src;
+    }
+
+    String handleRequest(JsonRequest request) throws Exception {
+	String output = "";
+
+	long starttime = 0;
+	long endtime = 0;
+	long period = 0;
+
+	starttime = request.getNumber("start", 0);
+	endtime = request.getNumber("end", 0);
+	period = request.getNumber("period", 0);
+
+	if (starttime == 0) { starttime = source.getStartTime(); }
+	if (endtime == 0) { 
+	    if (period > 0) {
+		endtime = starttime + period;
+	    } else {
+		endtime = source.getEndTime(); 
+	    }
+	}
+	
+	LogIterator iter = source.iterator(starttime, endtime);
+	JSONObject data = new JSONObject();
+	data.put("startTime", starttime);
+	data.put("endTime", endtime);
+	long size = 0;
+	
+	size = iter.size();
+	
+	data.put("numEntries",  size);
+	if (LOG.isDebugEnabled()) {
+	    LOG.debug("handle(start= " + starttime + ", end=" + endtime + ", numEntries=" + size +")");
+	}
+	return JSONValue.toJSONString(data);
+    }
+}
+

Added: hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/servlets/StaticContent.java
URL: http://svn.apache.org/viewvc/hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/servlets/StaticContent.java?rev=953041&view=auto
==============================================================================
--- hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/servlets/StaticContent.java (added)
+++ hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/servlets/StaticContent.java Wed Jun  9 15:26:22 2010
@@ -0,0 +1,50 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.zookeeper.graph.servlets;
+
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.BufferedReader;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+public class StaticContent extends HttpServlet {
+    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+    {
+	String path = request.getRequestURI().substring(request.getServletPath().length());
+
+	InputStream resource = ClassLoader.getSystemResourceAsStream("org/apache/zookeeper/graph/resources" + path);	  
+	if (resource == null) {
+	    response.getWriter().println(path + " not found!");
+	    response.setStatus(HttpServletResponse.SC_NOT_FOUND);
+	    return;
+	}
+	
+	while (resource.available() > 0) {
+	    response.getWriter().write(resource.read());
+	}
+	//        response.setContentType("text/plain;charset=utf-8");
+        response.setStatus(HttpServletResponse.SC_OK);
+    }
+
+}

Added: hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/servlets/Throughput.java
URL: http://svn.apache.org/viewvc/hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/servlets/Throughput.java?rev=953041&view=auto
==============================================================================
--- hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/servlets/Throughput.java (added)
+++ hadoop/zookeeper/trunk/src/contrib/loggraph/src/java/org/apache/zookeeper/graph/servlets/Throughput.java Wed Jun  9 15:26:22 2010
@@ -0,0 +1,124 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.zookeeper.graph.servlets;
+
+import java.io.IOException;
+import java.io.BufferedOutputStream;
+import java.io.FileOutputStream;
+import java.io.DataOutputStream;
+import java.io.PrintStream;
+
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+
+import org.apache.zookeeper.graph.*;
+import org.apache.log4j.Logger;
+
+import org.json.simple.JSONArray;
+import org.json.simple.JSONObject;
+import org.json.simple.JSONValue;
+
+
+public class Throughput extends JsonServlet
+{
+    private static final int MS_PER_SEC = 1000;
+    private static final int MS_PER_MIN = MS_PER_SEC*60;
+    private static final int MS_PER_HOUR = MS_PER_MIN*60;
+
+    private LogSource source = null;
+
+    public Throughput(LogSource src) throws Exception {
+	this.source = src; 
+    }
+
+    public String handleRequest(JsonRequest request) throws Exception {
+	long starttime = 0;
+	long endtime = 0;
+	long period = 0;
+	long scale = 0;
+	
+	starttime = request.getNumber("start", 0);
+	endtime = request.getNumber("end", 0);
+	period = request.getNumber("period", 0);
+	
+
+	if (starttime == 0) { starttime = source.getStartTime(); }
+	if (endtime == 0) { 
+	    if (period > 0) {
+		endtime = starttime + period;
+	    } else {
+		endtime = source.getEndTime(); 
+	    }
+	}
+	
+	String scalestr = request.getString("scale", "minutes");
+	if (scalestr.equals("seconds")) {
+	    scale = MS_PER_SEC;
+	} else if (scalestr.equals("hours")) {
+	    scale = MS_PER_HOUR;
+	} else {
+	    scale = MS_PER_MIN;
+	} 	
+	
+	LogIterator iter = source.iterator(starttime, endtime);
+	
+	long current = 0;
+	long currentms = 0;
+	HashSet<Long> zxids_ms = new HashSet<Long>();
+	long zxidcount = 0;
+
+	JSONArray events = new JSONArray();
+	while (iter.hasNext()) {
+	    LogEntry e = iter.next();
+	    if (e.getType() != LogEntry.Type.TXN) {
+		continue;
+	    }
+
+	    TransactionEntry cxn = (TransactionEntry)e;
+	    
+	    long ms = cxn.getTimestamp();
+	    long inscale = ms/scale;
+
+	    if (currentms != ms && currentms != 0) {
+		zxidcount += zxids_ms.size();
+		zxids_ms.clear();
+	    }
+
+	    if (inscale != current && current != 0) {
+		JSONObject o = new JSONObject();
+		o.put("time", current*scale);
+		o.put("count", zxidcount);
+		events.add(o);
+		zxidcount = 0;
+	    }
+	    current = inscale;
+	    currentms = ms;
+
+	    zxids_ms.add(cxn.getZxid());
+	}
+	JSONObject o = new JSONObject();
+	o.put("time", current*scale);
+	o.put("count", zxidcount);
+	events.add(o);
+
+	iter.close();
+	
+	return JSONValue.toJSONString(events);
+    }
+
+};

Added: hadoop/zookeeper/trunk/src/contrib/loggraph/web/org/apache/zookeeper/graph/log4j.properties
URL: http://svn.apache.org/viewvc/hadoop/zookeeper/trunk/src/contrib/loggraph/web/org/apache/zookeeper/graph/log4j.properties?rev=953041&view=auto
==============================================================================
--- hadoop/zookeeper/trunk/src/contrib/loggraph/web/org/apache/zookeeper/graph/log4j.properties (added)
+++ hadoop/zookeeper/trunk/src/contrib/loggraph/web/org/apache/zookeeper/graph/log4j.properties Wed Jun  9 15:26:22 2010
@@ -0,0 +1,11 @@
+log4j.rootLogger=TRACE, CONSOLE
+
+# Print the date in ISO 8601 format
+log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
+log4j.appender.CONSOLE.Threshold=TRACE
+log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
+log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} - %-5p [%t:%C{1}@%L] - %m%n
+
+log4j.logger.org.apache.zookeeper.graph.LogSkipList=off
+log4j.logger.org.apache.zookeeper.graph.RandomAccessFileReader=off
+#log4j.logger.org.apache.zookeeper.graph.Log4JSource=off
\ No newline at end of file

Added: hadoop/zookeeper/trunk/src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/date.format.js
URL: http://svn.apache.org/viewvc/hadoop/zookeeper/trunk/src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/date.format.js?rev=953041&view=auto
==============================================================================
--- hadoop/zookeeper/trunk/src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/date.format.js (added)
+++ hadoop/zookeeper/trunk/src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/date.format.js Wed Jun  9 15:26:22 2010
@@ -0,0 +1,126 @@
+/*
+ * Date Format 1.2.3
+ * (c) 2007-2009 Steven Levithan <stevenlevithan.com>
+ * MIT license
+ *
+ * Includes enhancements by Scott Trenda <scott.trenda.net>
+ * and Kris Kowal <cixar.com/~kris.kowal/>
+ *
+ * Accepts a date, a mask, or a date and a mask.
+ * Returns a formatted version of the given date.
+ * The date defaults to the current date/time.
+ * The mask defaults to dateFormat.masks.default.
+ */
+
+var dateFormat = function () {
+	var	token = /d{1,4}|m{1,4}|yy(?:yy)?|([HhMsTt])\1?|[LloSZ]|"[^"]*"|'[^']*'/g,
+		timezone = /\b(?:[PMCEA][SDP]T|(?:Pacific|Mountain|Central|Eastern|Atlantic) (?:Standard|Daylight|Prevailing) Time|(?:GMT|UTC)(?:[-+]\d{4})?)\b/g,
+		timezoneClip = /[^-+\dA-Z]/g,
+		pad = function (val, len) {
+			val = String(val);
+			len = len || 2;
+			while (val.length < len) val = "0" + val;
+			return val;
+		};
+
+	// Regexes and supporting functions are cached through closure
+	return function (date, mask, utc) {
+		var dF = dateFormat;
+
+		// You can't provide utc if you skip other args (use the "UTC:" mask prefix)
+		if (arguments.length == 1 && Object.prototype.toString.call(date) == "[object String]" && !/\d/.test(date)) {
+			mask = date;
+			date = undefined;
+		}
+
+		// Passing date through Date applies Date.parse, if necessary
+		date = date ? new Date(date) : new Date;
+		if (isNaN(date)) throw SyntaxError("invalid date");
+
+		mask = String(dF.masks[mask] || mask || dF.masks["default"]);
+
+		// Allow setting the utc argument via the mask
+		if (mask.slice(0, 4) == "UTC:") {
+			mask = mask.slice(4);
+			utc = true;
+		}
+
+		var	_ = utc ? "getUTC" : "get",
+			d = date[_ + "Date"](),
+			D = date[_ + "Day"](),
+			m = date[_ + "Month"](),
+			y = date[_ + "FullYear"](),
+			H = date[_ + "Hours"](),
+			M = date[_ + "Minutes"](),
+			s = date[_ + "Seconds"](),
+			L = date[_ + "Milliseconds"](),
+			o = utc ? 0 : date.getTimezoneOffset(),
+			flags = {
+				d:    d,
+				dd:   pad(d),
+				ddd:  dF.i18n.dayNames[D],
+				dddd: dF.i18n.dayNames[D + 7],
+				m:    m + 1,
+				mm:   pad(m + 1),
+				mmm:  dF.i18n.monthNames[m],
+				mmmm: dF.i18n.monthNames[m + 12],
+				yy:   String(y).slice(2),
+				yyyy: y,
+				h:    H % 12 || 12,
+				hh:   pad(H % 12 || 12),
+				H:    H,
+				HH:   pad(H),
+				M:    M,
+				MM:   pad(M),
+				s:    s,
+				ss:   pad(s),
+				l:    pad(L, 3),
+				L:    pad(L > 99 ? Math.round(L / 10) : L),
+				t:    H < 12 ? "a"  : "p",
+				tt:   H < 12 ? "am" : "pm",
+				T:    H < 12 ? "A"  : "P",
+				TT:   H < 12 ? "AM" : "PM",
+				Z:    utc ? "UTC" : (String(date).match(timezone) || [""]).pop().replace(timezoneClip, ""),
+				o:    (o > 0 ? "-" : "+") + pad(Math.floor(Math.abs(o) / 60) * 100 + Math.abs(o) % 60, 4),
+				S:    ["th", "st", "nd", "rd"][d % 10 > 3 ? 0 : (d % 100 - d % 10 != 10) * d % 10]
+			};
+
+		return mask.replace(token, function ($0) {
+			return $0 in flags ? flags[$0] : $0.slice(1, $0.length - 1);
+		});
+	};
+}();
+
+// Some common format strings
+dateFormat.masks = {
+	"default":      "ddd mmm dd yyyy HH:MM:ss",
+	shortDate:      "m/d/yy",
+	mediumDate:     "mmm d, yyyy",
+	longDate:       "mmmm d, yyyy",
+	fullDate:       "dddd, mmmm d, yyyy",
+	shortTime:      "h:MM TT",
+	mediumTime:     "h:MM:ss TT",
+	longTime:       "h:MM:ss TT Z",
+	isoDate:        "yyyy-mm-dd",
+	isoTime:        "HH:MM:ss",
+	isoDateTime:    "yyyy-mm-dd'T'HH:MM:ss",
+	isoUtcDateTime: "UTC:yyyy-mm-dd'T'HH:MM:ss'Z'"
+};
+
+// Internationalization strings
+dateFormat.i18n = {
+	dayNames: [
+		"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
+		"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
+	],
+	monthNames: [
+		"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
+		"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
+	]
+};
+
+// For convenience...
+Date.prototype.format = function (mask, utc) {
+	return dateFormat(this, mask, utc);
+};
+

Added: hadoop/zookeeper/trunk/src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/g.bar.js
URL: http://svn.apache.org/viewvc/hadoop/zookeeper/trunk/src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/g.bar.js?rev=953041&view=auto
==============================================================================
--- hadoop/zookeeper/trunk/src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/g.bar.js (added)
+++ hadoop/zookeeper/trunk/src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/g.bar.js Wed Jun  9 15:26:22 2010
@@ -0,0 +1,385 @@
+/*
+ * g.Raphael 0.4 - Charting library, based on Raphaël
+ *
+ * Copyright (c) 2009 Dmitry Baranovskiy (http://g.raphaeljs.com)
+ * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license.
+ */
+Raphael.fn.g.barchart = function (x, y, width, height, values, opts) {
+    opts = opts || {};
+    var type = {round: "round", sharp: "sharp", soft: "soft"}[opts.type] || "square",
+        gutter = parseFloat(opts.gutter || "20%"),
+        chart = this.set(),
+        bars = this.set(),
+        covers = this.set(),
+        covers2 = this.set(),
+        total = Math.max.apply(Math, values),
+        stacktotal = [],
+        paper = this,
+        multi = 0,
+        colors = opts.colors || this.g.colors,
+        len = values.length;
+    if (this.raphael.is(values[0], "array")) {
+        total = [];
+        multi = len;
+        len = 0;
+        for (var i = values.length; i--;) {
+            bars.push(this.set());
+            total.push(Math.max.apply(Math, values[i]));
+            len = Math.max(len, values[i].length);
+        }
+        if (opts.stacked) {
+            for (var i = len; i--;) {
+                var tot = 0;
+                for (var j = values.length; j--;) {
+                    tot +=+ values[j][i] || 0;
+                }
+                stacktotal.push(tot);
+            }
+        }
+        for (var i = values.length; i--;) {
+            if (values[i].length < len) {
+                for (var j = len; j--;) {
+                    values[i].push(0);
+                }
+            }
+        }
+        total = Math.max.apply(Math, opts.stacked ? stacktotal : total);
+    }
+    
+    total = (opts.to) || total;
+    var barwidth = width / (len * (100 + gutter) + gutter) * 100,
+        barhgutter = barwidth * gutter / 100,
+        barvgutter = opts.vgutter == null ? 20 : opts.vgutter,
+        stack = [],
+        X = x + barhgutter,
+        Y = (height - 2 * barvgutter) / total;
+    if (!opts.stretch) {
+        barhgutter = Math.round(barhgutter);
+        barwidth = Math.floor(barwidth);
+    }
+    !opts.stacked && (barwidth /= multi || 1);
+    for (var i = 0; i < len; i++) {
+        stack = [];
+        for (var j = 0; j < (multi || 1); j++) {
+            var h = Math.round((multi ? values[j][i] : values[i]) * Y),
+                top = y + height - barvgutter - h,
+                bar = this.g.finger(Math.round(X + barwidth / 2), top + h, barwidth, h, true, type).attr({stroke: colors[multi ? j : i], fill: colors[multi ? j : i]});
+            if (multi) {
+                bars[j].push(bar);
+            } else {
+                bars.push(bar);
+            }
+            bar.y = top;
+            bar.x = Math.round(X + barwidth / 2);
+            bar.w = barwidth;
+            bar.h = h;
+            bar.value = multi ? values[j][i] : values[i];
+            if (!opts.stacked) {
+                X += barwidth;
+            } else {
+                stack.push(bar);
+            }
+        }
+        if (opts.stacked) {
+            var cvr;
+            covers2.push(cvr = this.rect(stack[0].x - stack[0].w / 2, y, barwidth, height).attr(this.g.shim));
+            cvr.bars = this.set();
+            var size = 0;
+            for (var s = stack.length; s--;) {
+                stack[s].toFront();
+            }
+            for (var s = 0, ss = stack.length; s < ss; s++) {
+                var bar = stack[s],
+                    cover,
+                    h = (size + bar.value) * Y,
+                    path = this.g.finger(bar.x, y + height - barvgutter - !!size * .5, barwidth, h, true, type, 1);
+                cvr.bars.push(bar);
+                size && bar.attr({path: path});
+                bar.h = h;
+                bar.y = y + height - barvgutter - !!size * .5 - h;
+                covers.push(cover = this.rect(bar.x - bar.w / 2, bar.y, barwidth, bar.value * Y).attr(this.g.shim));
+                cover.bar = bar;
+                cover.value = bar.value;
+                size += bar.value;
+            }
+            X += barwidth;
+        }
+        X += barhgutter;
+    }
+    covers2.toFront();
+    X = x + barhgutter;
+    if (!opts.stacked) {
+        for (var i = 0; i < len; i++) {
+            for (var j = 0; j < (multi || 1); j++) {
+                var cover;
+                covers.push(cover = this.rect(Math.round(X), y + barvgutter, barwidth, height - barvgutter).attr(this.g.shim));
+                cover.bar = multi ? bars[j][i] : bars[i];
+                cover.value = cover.bar.value;
+                X += barwidth;
+            }
+            X += barhgutter;
+        }
+    }
+    chart.label = function (labels, isBottom) {
+        labels = labels || [];
+        this.labels = paper.set();
+        var L, l = -Infinity;
+        if (opts.stacked) {
+            for (var i = 0; i < len; i++) {
+                var tot = 0;
+                for (var j = 0; j < (multi || 1); j++) {
+                    tot += multi ? values[j][i] : values[i];
+                    if (j == multi - 1) {
+                        var label = paper.g.labelise(labels[i], tot, total);
+                        L = paper.g.text(bars[i * (multi || 1) + j].x, y + height - barvgutter / 2, label).insertBefore(covers[i * (multi || 1) + j]);
+                        var bb = L.getBBox();
+                        if (bb.x - 7 < l) {
+                            L.remove();
+                        } else {
+                            this.labels.push(L);
+                            l = bb.x + bb.width;
+                        }
+                    }
+                }
+            }
+        } else {
+            for (var i = 0; i < len; i++) {
+                for (var j = 0; j < (multi || 1); j++) {
+                    var label = paper.g.labelise(multi ? labels[j] && labels[j][i] : labels[i], multi ? values[j][i] : values[i], total);
+                    L = paper.g.text(bars[i * (multi || 1) + j].x, isBottom ? y + height - barvgutter / 2 : bars[i * (multi || 1) + j].y - 10, label).insertBefore(covers[i * (multi || 1) + j]);
+                    var bb = L.getBBox();
+                    if (bb.x - 7 < l) {
+                        L.remove();
+                    } else {
+                        this.labels.push(L);
+                        l = bb.x + bb.width;
+                    }
+                }
+            }
+        }
+        return this;
+    };
+    chart.hover = function (fin, fout) {
+        covers2.hide();
+        covers.show();
+        covers.mouseover(fin).mouseout(fout);
+        return this;
+    };
+    chart.hoverColumn = function (fin, fout) {
+        covers.hide();
+        covers2.show();
+        fout = fout || function () {};
+        covers2.mouseover(fin).mouseout(fout);
+        return this;
+    };
+    chart.click = function (f) {
+        covers2.hide();
+        covers.show();
+        covers.click(f);
+        return this;
+    };
+    chart.each = function (f) {
+        if (!Raphael.is(f, "function")) {
+            return this;
+        }
+        for (var i = covers.length; i--;) {
+            f.call(covers[i]);
+        }
+        return this;
+    };
+    chart.eachColumn = function (f) {
+        if (!Raphael.is(f, "function")) {
+            return this;
+        }
+        for (var i = covers2.length; i--;) {
+            f.call(covers2[i]);
+        }
+        return this;
+    };
+    chart.clickColumn = function (f) {
+        covers.hide();
+        covers2.show();
+        covers2.click(f);
+        return this;
+    };
+    chart.push(bars, covers, covers2);
+    chart.bars = bars;
+    chart.covers = covers;
+    return chart;
+};
+Raphael.fn.g.hbarchart = function (x, y, width, height, values, opts) {
+    opts = opts || {};
+    var type = {round: "round", sharp: "sharp", soft: "soft"}[opts.type] || "square",
+        gutter = parseFloat(opts.gutter || "20%"),
+        chart = this.set(),
+        bars = this.set(),
+        covers = this.set(),
+        covers2 = this.set(),
+        total = Math.max.apply(Math, values),
+        stacktotal = [],
+        paper = this,
+        multi = 0,
+        colors = opts.colors || this.g.colors,
+        len = values.length;
+    if (this.raphael.is(values[0], "array")) {
+        total = [];
+        multi = len;
+        len = 0;
+        for (var i = values.length; i--;) {
+            bars.push(this.set());
+            total.push(Math.max.apply(Math, values[i]));
+            len = Math.max(len, values[i].length);
+        }
+        if (opts.stacked) {
+            for (var i = len; i--;) {
+                var tot = 0;
+                for (var j = values.length; j--;) {
+                    tot +=+ values[j][i] || 0;
+                }
+                stacktotal.push(tot);
+            }
+        }
+        for (var i = values.length; i--;) {
+            if (values[i].length < len) {
+                for (var j = len; j--;) {
+                    values[i].push(0);
+                }
+            }
+        }
+        total = Math.max.apply(Math, opts.stacked ? stacktotal : total);
+    }
+    
+    total = (opts.to) || total;
+    var barheight = Math.floor(height / (len * (100 + gutter) + gutter) * 100),
+        bargutter = Math.floor(barheight * gutter / 100),
+        stack = [],
+        Y = y + bargutter,
+        X = (width - 1) / total;
+    !opts.stacked && (barheight /= multi || 1);
+    for (var i = 0; i < len; i++) {
+        stack = [];
+        for (var j = 0; j < (multi || 1); j++) {
+            var val = multi ? values[j][i] : values[i],
+                bar = this.g.finger(x, Y + barheight / 2, Math.round(val * X), barheight - 1, false, type).attr({stroke: colors[multi ? j : i], fill: colors[multi ? j : i]});
+            if (multi) {
+                bars[j].push(bar);
+            } else {
+                bars.push(bar);
+            }
+            bar.x = x + Math.round(val * X);
+            bar.y = Y + barheight / 2;
+            bar.w = Math.round(val * X);
+            bar.h = barheight;
+            bar.value = +val;
+            if (!opts.stacked) {
+                Y += barheight;
+            } else {
+                stack.push(bar);
+            }
+        }
+        if (opts.stacked) {
+            var cvr = this.rect(x, stack[0].y - stack[0].h / 2, width, barheight).attr(this.g.shim);
+            covers2.push(cvr);
+            cvr.bars = this.set();
+            var size = 0;
+            for (var s = stack.length; s--;) {
+                stack[s].toFront();
+            }
+            for (var s = 0, ss = stack.length; s < ss; s++) {
+                var bar = stack[s],
+                    cover,
+                    val = Math.round((size + bar.value) * X),
+                    path = this.g.finger(x, bar.y, val, barheight - 1, false, type, 1);
+                cvr.bars.push(bar);
+                size && bar.attr({path: path});
+                bar.w = val;
+                bar.x = x + val;
+                covers.push(cover = this.rect(x + size * X, bar.y - bar.h / 2, bar.value * X, barheight).attr(this.g.shim));
+                cover.bar = bar;
+                size += bar.value;
+            }
+            Y += barheight;
+        }
+        Y += bargutter;
+    }
+    covers2.toFront();
+    Y = y + bargutter;
+    if (!opts.stacked) {
+        for (var i = 0; i < len; i++) {
+            for (var j = 0; j < multi; j++) {
+                var cover = this.rect(x, Y, width, barheight).attr(this.g.shim);
+                covers.push(cover);
+                cover.bar = bars[j][i];
+                Y += barheight;
+            }
+            Y += bargutter;
+        }
+    }
+    chart.label = function (labels, isRight) {
+        labels = labels || [];
+        this.labels = paper.set();
+        for (var i = 0; i < len; i++) {
+            for (var j = 0; j < multi; j++) {
+                var  label = paper.g.labelise(multi ? labels[j] && labels[j][i] : labels[i], multi ? values[j][i] : values[i], total);
+                var X = isRight ? bars[i * (multi || 1) + j].x - barheight / 2 + 3 : x + 5,
+                    A = isRight ? "end" : "start",
+                    L;
+                this.labels.push(L = paper.g.text(X, bars[i * (multi || 1) + j].y, label).attr({"text-anchor": A}).insertBefore(covers[0]));
+                if (L.getBBox().x < x + 5) {
+                    L.attr({x: x + 5, "text-anchor": "start"});
+                } else {
+                    bars[i * (multi || 1) + j].label = L;
+                }
+            }
+        }
+        return this;
+    };
+    chart.hover = function (fin, fout) {
+        covers2.hide();
+        covers.show();
+        fout = fout || function () {};
+        covers.mouseover(fin).mouseout(fout);
+        return this;
+    };
+    chart.hoverColumn = function (fin, fout) {
+        covers.hide();
+        covers2.show();
+        fout = fout || function () {};
+        covers2.mouseover(fin).mouseout(fout);
+        return this;
+    };
+    chart.each = function (f) {
+        if (!Raphael.is(f, "function")) {
+            return this;
+        }
+        for (var i = covers.length; i--;) {
+            f.call(covers[i]);
+        }
+        return this;
+    };
+    chart.eachColumn = function (f) {
+        if (!Raphael.is(f, "function")) {
+            return this;
+        }
+        for (var i = covers2.length; i--;) {
+            f.call(covers2[i]);
+        }
+        return this;
+    };
+    chart.click = function (f) {
+        covers2.hide();
+        covers.show();
+        covers.click(f);
+        return this;
+    };
+    chart.clickColumn = function (f) {
+        covers.hide();
+        covers2.show();
+        covers2.click(f);
+        return this;
+    };
+    chart.push(bars, covers, covers2);
+    chart.bars = bars;
+    chart.covers = covers;
+    return chart;
+};

Added: hadoop/zookeeper/trunk/src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/g.dot.js
URL: http://svn.apache.org/viewvc/hadoop/zookeeper/trunk/src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/g.dot.js?rev=953041&view=auto
==============================================================================
--- hadoop/zookeeper/trunk/src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/g.dot.js (added)
+++ hadoop/zookeeper/trunk/src/contrib/loggraph/web/org/apache/zookeeper/graph/resources/g.dot.js Wed Jun  9 15:26:22 2010
@@ -0,0 +1,110 @@
+/*
+ * g.Raphael 0.4 - Charting library, based on Raphaël
+ *
+ * Copyright (c) 2009 Dmitry Baranovskiy (http://g.raphaeljs.com)
+ * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license.
+ */
+Raphael.fn.g.dotchart = function (x, y, width, height, valuesx, valuesy, size, opts) {
+    function drawAxis(ax) {
+        +ax[0] && (ax[0] = paper.g.axis(x + gutter, y + gutter, width - 2 * gutter, minx, maxx, opts.axisxstep || Math.floor((width - 2 * gutter) / 20), 2, opts.axisxlabels || null, opts.axisxtype || "t"));
+        +ax[1] && (ax[1] = paper.g.axis(x + width - gutter, y + height - gutter, height - 2 * gutter, miny, maxy, opts.axisystep || Math.floor((height - 2 * gutter) / 20), 3, opts.axisylabels || null, opts.axisytype || "t"));
+        +ax[2] && (ax[2] = paper.g.axis(x + gutter, y + height - gutter + maxR, width - 2 * gutter, minx, maxx, opts.axisxstep || Math.floor((width - 2 * gutter) / 20), 0, opts.axisxlabels || null, opts.axisxtype || "t"));
+        +ax[3] && (ax[3] = paper.g.axis(x + gutter - maxR, y + height - gutter, height - 2 * gutter, miny, maxy, opts.axisystep || Math.floor((height - 2 * gutter) / 20), 1, opts.axisylabels || null, opts.axisytype || "t"));
+    }
+    opts = opts || {};
+    var xdim = this.g.snapEnds(Math.min.apply(Math, valuesx), Math.max.apply(Math, valuesx), valuesx.length - 1),
+        minx = xdim.from,
+        maxx = xdim.to,
+        gutter = opts.gutter || 10,
+        ydim = this.g.snapEnds(Math.min.apply(Math, valuesy), Math.max.apply(Math, valuesy), valuesy.length - 1),
+        miny = ydim.from,
+        maxy = ydim.to,
+        len = Math.max(valuesx.length, valuesy.length, size.length),
+        symbol = this.g.markers[opts.symbol] || "disc",
+        res = this.set(),
+        series = this.set(),
+        max = opts.max || 100,
+        top = Math.max.apply(Math, size),
+        R = [],
+        paper = this,
+        k = Math.sqrt(top / Math.PI) * 2 / max;
+
+    for (var i = 0; i < len; i++) {
+        R[i] = Math.min(Math.sqrt(size[i] / Math.PI) * 2 / k, max);
+    }
+    gutter = Math.max.apply(Math, R.concat(gutter));
+    var axis = this.set(),
+        maxR = Math.max.apply(Math, R);
+    if (opts.axis) {
+        var ax = (opts.axis + "").split(/[,\s]+/);
+        drawAxis(ax);
+        var g = [], b = [];
+        for (var i = 0, ii = ax.length; i < ii; i++) {
+            var bb = ax[i].all ? ax[i].all.getBBox()[["height", "width"][i % 2]] : 0;
+            g[i] = bb + gutter;
+            b[i] = bb;
+        }
+        gutter = Math.max.apply(Math, g.concat(gutter));
+        for (var i = 0, ii = ax.length; i < ii; i++) if (ax[i].all) {
+            ax[i].remove();
+            ax[i] = 1;
+        }
+        drawAxis(ax);
+        for (var i = 0, ii = ax.length; i < ii; i++) if (ax[i].all) {
+            axis.push(ax[i].all);
+        }
+        res.axis = axis;
+    }
+    var kx = (width - gutter * 2) / ((maxx - minx) || 1),
+        ky = (height - gutter * 2) / ((maxy - miny) || 1);
+    for (var i = 0, ii = valuesy.length; i < ii; i++) {
+        var sym = this.raphael.is(symbol, "array") ? symbol[i] : symbol,
+            X = x + gutter + (valuesx[i] - minx) * kx,
+            Y = y + height - gutter - (valuesy[i] - miny) * ky;
+        sym && R[i] && series.push(this.g[sym](X, Y, R[i]).attr({fill: opts.heat ? this.g.colorValue(R[i], maxR) : Raphael.fn.g.colors[0], "fill-opacity": opts.opacity ? R[i] / max : 1, stroke: "none"}));
+    }
+    var covers = this.set();
+    for (var i = 0, ii = valuesy.length; i < ii; i++) {
+        var X = x + gutter + (valuesx[i] - minx) * kx,
+            Y = y + height - gutter - (valuesy[i] - miny) * ky;
+        covers.push(this.circle(X, Y, maxR).attr(this.g.shim));
+        opts.href && opts.href[i] && covers[i].attr({href: opts.href[i]});
+        covers[i].r = +R[i].toFixed(3);
+        covers[i].x = +X.toFixed(3);
+        covers[i].y = +Y.toFixed(3);
+        covers[i].X = valuesx[i];
+        covers[i].Y = valuesy[i];
+        covers[i].value = size[i] || 0;
+        covers[i].dot = series[i];
+    }
+    res.covers = covers;
+    res.series = series;
+    res.push(series, axis, covers);
+    res.hover = function (fin, fout) {
+        covers.mouseover(fin).mouseout(fout);
+        return this;
+    };
+    res.click = function (f) {
+        covers.click(f);
+        return this;
+    };
+    res.each = function (f) {
+        if (!Raphael.is(f, "function")) {
+            return this;
+        }
+        for (var i = covers.length; i--;) {
+            f.call(covers[i]);
+        }
+        return this;
+    };
+    res.href = function (map) {
+        var cover;
+        for (var i = covers.length; i--;) {
+            cover = covers[i];
+            if (cover.X == map.x && cover.Y == map.y && cover.value == map.value) {
+                cover.attr({href: map.href});
+            }
+        }
+    };
+    return res;
+};