You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by ju...@apache.org on 2007/05/22 12:26:34 UTC

svn commit: r540521 [2/3] - in /jackrabbit/trunk/contrib/jcrlog: ./ src/ src/main/ src/main/java/ src/main/java/org/ src/main/java/org/apache/ src/main/java/org/apache/jackrabbit/ src/main/java/org/apache/jackrabbit/jcrlog/ src/main/java/org/apache/jac...

Added: jackrabbit/trunk/contrib/jcrlog/src/main/java/org/apache/jackrabbit/jcrlog/RepositoryLogger.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/jcrlog/src/main/java/org/apache/jackrabbit/jcrlog/RepositoryLogger.java?view=auto&rev=540521
==============================================================================
--- jackrabbit/trunk/contrib/jcrlog/src/main/java/org/apache/jackrabbit/jcrlog/RepositoryLogger.java (added)
+++ jackrabbit/trunk/contrib/jcrlog/src/main/java/org/apache/jackrabbit/jcrlog/RepositoryLogger.java Tue May 22 03:26:30 2007
@@ -0,0 +1,240 @@
+/*
+ * 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.jackrabbit.jcrlog;
+
+import java.util.Properties;
+
+import javax.jcr.Credentials;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.apache.jackrabbit.core.TransientRepository;
+
+/**
+ * The public Repository object of the wrapper. Actually does not need to be
+ * used directly by the application (if the RepositoryFactory is used).
+ *
+ * @author Thomas Mueller
+ *
+ */
+public class RepositoryLogger extends LogObject implements Repository {
+
+    private Repository repository;
+
+    /**
+     * Open a repository and wrap it using the RepositoryLogger wrapper.
+     *
+     * @param url the URL
+     * @return the wrapped repository
+     * @throws RepositoryException
+     */
+    public static Repository open(String url) throws RepositoryException {
+        String wrappedURL;
+        int idx = url.toLowerCase().indexOf("url=");
+        if (idx < 0) {
+            wrappedURL = "apache/jackrabbit/transient";
+        } else {
+            wrappedURL = url.substring(idx + "url=".length());
+            url = url.substring(0, idx);
+        }
+        if (url == null || url.length() == 0) {
+            url = "sysout=true";
+        }
+        Properties prop = parseSettings(url);
+        Repository rep = RepositoryFactory.open(wrappedURL);
+        return new RepositoryLogger(rep, prop, wrappedURL);
+    }
+
+    /**
+     * Wrap a repository.
+     *
+     * @param repository
+     * @param settings the settings to use
+     * @return the wrapped repository
+     * @throws RepositoryException
+     */
+    public static Repository wrap(Repository repository, String settings)
+            throws RepositoryException {
+        String url;
+        if (repository instanceof TransientRepository) {
+            url = "apache/jackrabbit/transient";
+        } else {
+            // TODO support other repositories
+            url = "<unknown url, class: " + repository.getClass().getName()
+                    + ">";
+        }
+        if (settings == null || settings.length() == 0) {
+            settings = "sysout=true";
+        }
+        Properties prop = parseSettings(settings);
+        return new RepositoryLogger(repository, prop, url);
+    }
+
+    private RepositoryLogger(Repository repository, Properties prop, String url)
+            throws RepositoryException {
+        this.repository = repository;
+        String fileName = prop.getProperty("file");
+        String sysOut = prop.getProperty("sysout");
+        String logReturn = prop.getProperty("return");
+        String logCaller = prop.getProperty("caller");
+        String cast = prop.getProperty("cast");
+        String stream = prop.getProperty("stream");
+        Log log = new Log(fileName, parseBoolean(sysOut));
+        log.setLogCaller(parseBoolean(logCaller));
+        log.setLogReturn(parseBoolean(logReturn));
+        log.setCastToRealApi(parseBoolean(cast));
+        log.setLogStream(parseBoolean(stream));
+        String call = RepositoryFactory.CLASSNAME + ".open("
+                + StringUtils.quoteString(url) + ");";
+        int nextId = getNextId(LogObject.INTERFACE_DEF_REPOSITORY.type);
+        setLog(log, LogObject.INTERFACE_DEF_REPOSITORY.type, nextId);
+        logStartCall(getAssign(LogObject.INTERFACE_DEF_REPOSITORY.type, nextId, call));
+    }
+
+    private boolean parseBoolean(String s) {
+        return Boolean.valueOf(s).booleanValue();
+    }
+
+    private static Properties parseSettings(String settings)
+            throws RepositoryException {
+        Properties prop = new Properties();
+        String[] list = StringUtils.arraySplit(settings, ';');
+        for (int i = 0; list != null && i < list.length; i++) {
+            String setting = list[i];
+            if (setting.length() == 0) {
+                continue;
+            }
+            int equal = setting.indexOf('=');
+            if (equal < 0) {
+                throw new RepositoryException("Invalid settings format: "
+                        + settings);
+            }
+            String value = setting.substring(equal + 1).trim();
+            String key = setting.substring(0, equal).trim();
+            key = key.toLowerCase();
+            String old = prop.getProperty(key);
+            if (old != null && !old.equals(value)) {
+                throw new RepositoryException("Duplicate settings: " + key);
+            }
+            prop.setProperty(key, value);
+        }
+        return prop;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String getDescriptor(String key) {
+        try {
+            logStart(-1, false, "getDescriptor", StringUtils.quoteString(key));
+            String result = repository.getDescriptor(key);
+            logReturn(String.class, result);
+            return result;
+        } catch (Throwable t) {
+            throw logRuntimeException(t);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String[] getDescriptorKeys() {
+        try {
+            logStart(-1, false, "getDescriptorKeys", "");
+            String[] result = repository.getDescriptorKeys();
+            logReturn(String[].class, result);
+            return result;
+        } catch (Throwable t) {
+            throw logRuntimeException(t);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Session login() throws RepositoryException {
+        try {
+            int assignType = LogObject.INTERFACE_DEF_SESSION.type;
+            int nextId = logStart(assignType, false, "login", "");
+            Session result = repository.login();
+            result = (Session) wrap(result, false, Session.class, assignType,
+                    nextId);
+            logReturn();
+            return result;
+        } catch (Throwable t) {
+            throw logAndConvert(t);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Session login(Credentials credentials) throws RepositoryException {
+        try {
+            int assignType = LogObject.INTERFACE_DEF_SESSION.type;
+            int nextId = logStart(assignType, false, "login", StringUtils
+                    .quoteCredentials(credentials));
+            Session result = repository.login(credentials);
+            result = (Session) wrap(result, false, Session.class, assignType,
+                    nextId);
+            logReturn();
+            return result;
+        } catch (Throwable t) {
+            throw logAndConvert(t);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Session login(String workspaceName) throws RepositoryException {
+        try {
+            int assignType = LogObject.INTERFACE_DEF_SESSION.type;
+            int nextId = logStart(assignType, false, "login", StringUtils
+                    .quoteString(workspaceName));
+            Session result = repository.login(workspaceName);
+            result = (Session) wrap(result, false, Session.class, assignType,
+                    nextId);
+            logReturn();
+            return result;
+        } catch (Throwable t) {
+            throw logAndConvert(t);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Session login(Credentials credentials, String workspaceName)
+            throws RepositoryException {
+        try {
+            int assignType = LogObject.INTERFACE_DEF_SESSION.type;
+            int nextId = logStart(assignType, false, "login", StringUtils
+                    .quoteCredentials(credentials)
+                    + ", " + StringUtils.quoteString(workspaceName));
+            Session result = repository.login(credentials, workspaceName);
+            result = (Session) wrap(result, false, Session.class, assignType,
+                    nextId);
+            logReturn();
+            return result;
+        } catch (Throwable t) {
+            throw logAndConvert(t);
+        }
+    }
+
+}

Propchange: jackrabbit/trunk/contrib/jcrlog/src/main/java/org/apache/jackrabbit/jcrlog/RepositoryLogger.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/trunk/contrib/jcrlog/src/main/java/org/apache/jackrabbit/jcrlog/StringUtils.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/jcrlog/src/main/java/org/apache/jackrabbit/jcrlog/StringUtils.java?view=auto&rev=540521
==============================================================================
--- jackrabbit/trunk/contrib/jcrlog/src/main/java/org/apache/jackrabbit/jcrlog/StringUtils.java (added)
+++ jackrabbit/trunk/contrib/jcrlog/src/main/java/org/apache/jackrabbit/jcrlog/StringUtils.java Tue May 22 03:26:30 2007
@@ -0,0 +1,533 @@
+/*
+ * 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.jackrabbit.jcrlog;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectOutputStream;
+import java.io.OutputStream;
+import java.lang.reflect.Proxy;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+
+import javax.jcr.Credentials;
+import javax.jcr.ItemVisitor;
+import javax.jcr.SimpleCredentials;
+import javax.jcr.observation.EventListener;
+
+/**
+ * Some String manipulations / formatting functions used by this tool.
+ *
+ * @author Thomas Mueller
+ *
+ */
+public class StringUtils {
+
+    private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat(
+            "yyyy-MM-dd HH:mm:ss.SSS Z");
+
+    static String[] arraySplit(String s, char separatorChar) {
+        if (s == null) {
+            return null;
+        }
+        if (s.length() == 0) {
+            return new String[0];
+        }
+        ArrayList list = new ArrayList();
+        StringBuffer buff = new StringBuffer(s.length());
+        for (int i = 0; i < s.length(); i++) {
+            char c = s.charAt(i);
+            if (c == separatorChar) {
+                list.add(buff.toString());
+                buff.setLength(0);
+            } else if (c == '\\' && i < s.length() - 1) {
+                buff.append(s.charAt(++i));
+            } else {
+                buff.append(c);
+            }
+        }
+        list.add(buff.toString());
+        String[] array = new String[list.size()];
+        list.toArray(array);
+        return array;
+    }
+
+    private static String addAsterix(String s, int index) {
+        if (s != null && index < s.length()) {
+            s = s.substring(0, index) + "[*]" + s.substring(index);
+        }
+        return s;
+    }
+
+    private static IllegalArgumentException getFormatException(String s, int i) {
+        return new IllegalArgumentException("String format error: "
+                + addAsterix(s, i));
+    }
+
+    /**
+     * Parse a a Java string.
+     *
+     * @param s the string formatted as a Java string
+     * @return the parsed string
+     */
+    public static String javaDecode(String s) {
+        StringBuffer buff = new StringBuffer(s.length());
+        for (int i = 0; i < s.length(); i++) {
+            char c = s.charAt(i);
+            if (c == '"') {
+                break;
+            } else if (c == '\\') {
+                if (i >= s.length()) {
+                    throw getFormatException(s, s.length() - 1);
+                }
+                c = s.charAt(++i);
+                switch (c) {
+                case 't':
+                    buff.append('\t');
+                    break;
+                case 'r':
+                    buff.append('\r');
+                    break;
+                case 'n':
+                    buff.append('\n');
+                    break;
+                case 'b':
+                    buff.append('\b');
+                    break;
+                case 'f':
+                    buff.append('\f');
+                    break;
+                case '"':
+                    buff.append('"');
+                    break;
+                case '\\':
+                    buff.append('\\');
+                    break;
+                case 'u': {
+                    try {
+                        c = (char) (Integer.parseInt(s.substring(i + 1, i + 5),
+                                16));
+                    } catch (NumberFormatException e) {
+                        throw getFormatException(s, i);
+                    }
+                    i += 4;
+                    buff.append(c);
+                    break;
+                }
+                default:
+                    if (c >= '0' && c <= '9') {
+                        try {
+                            c = (char) (Integer.parseInt(s.substring(i, i + 3),
+                                    8));
+                        } catch (NumberFormatException e) {
+                            throw getFormatException(s, i);
+                        }
+                        i += 2;
+                        buff.append(c);
+                    } else {
+                        throw getFormatException(s, i);
+                    }
+                }
+            } else {
+                buff.append(c);
+            }
+        }
+        return buff.toString();
+    }
+
+    private static String javaEncode(String s) {
+        StringBuffer buff = new StringBuffer(s.length());
+        for (int i = 0; i < s.length(); i++) {
+            char c = s.charAt(i);
+            switch (c) {
+            // case '\b':
+            // // BS backspace
+            // // not supported in properties files
+            // buff.append("\\b");
+            // break;
+            case '\t':
+                // HT horizontal tab
+                buff.append("\\t");
+                break;
+            case '\n':
+                // LF linefeed
+                buff.append("\\n");
+                break;
+            case '\f':
+                // FF form feed
+                buff.append("\\f");
+                break;
+            case '\r':
+                // CR carriage return
+                buff.append("\\r");
+                break;
+            case '"':
+                // double quote
+                buff.append("\\\"");
+                break;
+            case '\\':
+                // backslash
+                buff.append("\\\\");
+                break;
+            default:
+                int ch = (c & 0xffff);
+                if (ch >= ' ' && (ch < 0x80)) {
+                    buff.append(c);
+                    // not supported in properties files
+                    // } else if(ch < 0xff) {
+                    // buff.append("\\");
+                    // // make sure it's three characters (0x200 is octal
+                    // 1000)
+                    // buff.append(Integer.toOctalString(0x200 |
+                    // ch).substring(1));
+                } else {
+                    buff.append("\\u");
+                    // make sure it's four characters
+                    buff.append(Integer.toHexString(0x10000 | ch).substring(1));
+                }
+            }
+        }
+        return buff.toString();
+    }
+
+    static String quoteString(String result) {
+        if (result == null) {
+            return "null";
+        }
+        return "\"" + javaEncode(result) + "\"";
+    }
+
+    static String quoteArray(Class clazz, String name, Object[] array) {
+        if (array == null) {
+            return "null";
+        }
+        StringBuffer buff = new StringBuffer("new ");
+        buff.append(name);
+        buff.append("[]{");
+        for (int i = 0; i < array.length; i++) {
+            if (i > 0) {
+                buff.append(", ");
+            }
+            buff.append(quote(clazz, array[i]));
+        }
+        buff.append("}");
+        return buff.toString();
+    }
+
+    private static String quoteLongArray(long[] array) {
+        if (array == null) {
+            return "null";
+        }
+        StringBuffer buff = new StringBuffer("new long[]{");
+        for (int i = 0; i < array.length; i++) {
+            if (i > 0) {
+                buff.append(", ");
+            }
+            buff.append(array[i] + "L");
+        }
+        buff.append("}");
+        return buff.toString();
+    }
+
+    private static String quoteStringArray(String[] array) {
+        if (array == null) {
+            return "null";
+        }
+        StringBuffer buff = new StringBuffer("new String[]{");
+        for (int i = 0; i < array.length; i++) {
+            if (i > 0) {
+                buff.append(", ");
+            }
+            buff.append(quoteString(array[i]));
+        }
+        buff.append("}");
+        return buff.toString();
+    }
+
+    static String quoteCredentials(Credentials credentials) {
+        if (credentials == null) {
+            return null;
+        } else if (credentials instanceof SimpleCredentials) {
+            SimpleCredentials sc = (SimpleCredentials) credentials;
+            String user = sc.getUserID();
+            // TODO log attribute names
+            // String[] attributeNames = sc.getAttributeNames();
+            // TODO option to log the password
+            // String pwd = "- hidden -"; // new String(sc.getPassword());
+            return JcrUtils.CLASSNAME + ".createSimpleCredentials("
+                    + quoteString(user) + ")";
+        } else {
+            // TODO serialize credentials
+            return "?" + credentials.getClass().getName() + ":"
+                    + credentials.toString() + "?";
+        }
+    }
+
+    static String quoteArgs(Class[] argClasses, Object[] args) {
+        if (args == null) {
+            return "";
+        }
+        StringBuffer buff = new StringBuffer();
+        for (int i = 0; i < args.length; i++) {
+            if (i > 0) {
+                buff.append(", ");
+            }
+            buff.append(StringUtils.quote(argClasses[i], args[i]));
+        }
+        return buff.toString();
+    }
+
+    /**
+     * Format an object as Java source code.
+     *
+     * @param o the object
+     * @return the formatted string
+     */
+    public static String quoteSimple(Object o) {
+        if (o instanceof String) {
+            return quoteString((String) o);
+        } else if (o.getClass().isArray()) {
+            if (o instanceof String[]) {
+                return quoteStringArray((String[]) o);
+            } else if (o instanceof long[]) {
+                return quoteLongArray((long[]) o);
+            } else {
+                return null;
+            }
+        } else if (o instanceof Integer) {
+            return o.toString();
+        } else if (o instanceof Long) {
+            return o.toString() + "L";
+        } else if (o instanceof Boolean) {
+            return o.toString();
+        } else if (o instanceof Double) {
+            return o.toString() + "d";
+        } else if (o instanceof Calendar) {
+            return quoteCalendar((Calendar) o);
+        }
+        return null;
+    }
+
+    /**
+     * Format an object as Java source code.
+     *
+     * @param clazz the class
+     * @param o the object
+     * @return the formatted string
+     */
+    public static String quote(Class clazz, Object o) {
+        if (o == null) {
+            if (clazz == void.class) {
+                return "";
+            }
+            if (clazz.isArray()) {
+                return "(" + clazz.getComponentType().getName() + "[])null";
+            } else {
+                return "(" + clazz.getName() + ")null";
+            }
+        } else if (o instanceof Credentials) {
+            return quoteCredentials((Credentials) o);
+        } else if (o instanceof String) {
+            return quoteString((String) o);
+        } else if (o.getClass().isArray()) {
+            if (o instanceof String[]) {
+                return quoteStringArray((String[]) o);
+            } else if (o instanceof long[]) {
+                return quoteLongArray((long[]) o);
+            } else {
+                Class ct = o.getClass().getComponentType();
+                LogObject.InterfaceDef idef = LogObject.getInterface(ct);
+                if (idef == null) {
+                    throw new Error("unsupported class: " + o.getClass());
+                }
+                return quoteArray(idef.interfaceClass, idef.className,
+                        (Object[]) o);
+            }
+        } else if (o instanceof Integer) {
+            if (clazz == int.class) {
+                return o.toString();
+            } else {
+                // TODO doesn't work yet in player
+                return "new Integer(" + o.toString() + ")";
+            }
+        } else if (o instanceof Long) {
+            if (clazz == long.class) {
+                return o.toString() + "L";
+            } else {
+                // TODO doesn't work yet in player
+                return "new Long(" + o.toString() + ")";
+            }
+        } else if (o instanceof Boolean) {
+            if (clazz == boolean.class) {
+                return o.toString();
+            } else {
+                // TODO doesn't work yet in player
+                return "Boolean.valueOf(" + o.toString() + ")";
+            }
+        } else if (o instanceof EventListener) {
+            return JcrUtils.CLASSNAME
+                    + ".createDoNothingEventListener()";
+        } else if (o instanceof ItemVisitor) {
+            return JcrUtils.CLASSNAME
+                    + ".createDoNothingItemVisitor()";
+        } else if (o instanceof Proxy) {
+            LogObject obj = (LogObject) Proxy.getInvocationHandler(o);
+            return obj.getObjectName();
+        } else if (o instanceof LogObject) {
+            return ((LogObject) o).getObjectName();
+        } else if (o instanceof Double) {
+            return o.toString() + "d";
+        } else if (o instanceof Calendar) {
+            return quoteCalendar((Calendar) o);
+        } else if (o instanceof OutputStream) {
+            return quoteOutputStream((OutputStream) o);
+        } else if (o instanceof InputStream) {
+            return quoteInputStream((InputStream) o);
+        } else if (o instanceof org.xml.sax.ContentHandler) {
+            return JcrUtils.CLASSNAME
+            + ".createDoNothingEventListener()";
+        } else {
+            StringBuffer buff = new StringBuffer();
+            try {
+                String s = serializeToString(o);
+                buff.append("/* UNKNOWN OBJECT: ");
+                buff.append(o.toString());
+                buff.append(" */ (");
+                buff.append(o.getClass().getName());
+                buff.append(")");
+                buff.append(JcrUtils.CLASSNAME);
+                buff.append(".deserializeFromString(\"");
+                buff.append(s);
+                buff.append("\")");
+            } catch (Exception e) {
+                buff.append("/* UNKNOWN UNSERIALIZABLE OBJECT: ");
+                buff.append(o.toString());
+                buff.append(" (");
+                buff.append(o.getClass().getName());
+                buff.append(") */ null");
+            }
+            return buff.toString();
+        }
+    }
+
+    private static String quoteOutputStream(OutputStream stream) {
+        StringBuffer buff = new StringBuffer(JcrUtils.CLASSNAME);
+        buff.append(".getOutputStream()");
+        return buff.toString();
+    }
+
+    private static String quoteInputStream(InputStream stream) {
+        StringBuffer buff = new StringBuffer(JcrUtils.CLASSNAME);
+        buff.append(".getInputStream(\"");
+        buff.append(stream.toString());
+        buff.append("\")");
+        return buff.toString();
+    }
+
+    private static String quoteCalendar(Calendar cal) {
+        StringBuffer buff = new StringBuffer(JcrUtils.CLASSNAME);
+        buff.append(".getCalendar(\"");
+        buff.append(DATE_FORMAT.format(cal.getTime()));
+        buff.append("\")");
+        return buff.toString();
+    }
+
+    static Calendar parseCalendar(String s) {
+        Date d;
+        try {
+            d = DATE_FORMAT.parse(s);
+        } catch (ParseException e) {
+            throw new Error("Can not parse date: " + s);
+        }
+        // TODO calendar: timezones are not set here
+        Calendar cal = Calendar.getInstance();
+        cal.setTime(d);
+        return cal;
+    }
+
+    static String convertBytesToString(byte[] value) {
+        StringBuffer buff = new StringBuffer(value.length * 2);
+        for (int i = 0; value != null && i < value.length; i++) {
+            int c = value[i] & 0xff;
+            buff.append(Integer.toHexString((c >> 4) & 0xf));
+            buff.append(Integer.toHexString(c & 0xf));
+        }
+        return buff.toString();
+    }
+
+    static byte[] convertStringToBytes(String s) throws IllegalArgumentException, NumberFormatException {
+        int len = s.length();
+        if (len % 2 == 1) {
+            throw new IllegalArgumentException("Hex String with odd number of characters: " + s);
+        }
+        len /= 2;
+        byte[] buff = new byte[len];
+        for (int i = 0; i < len; i++) {
+            String t = s.substring(i + i, i + i + 2);
+            buff[i] = (byte) Integer.parseInt(t, 16);
+        }
+        return buff;
+    }
+
+    static String serializeToString(Object obj) throws IOException {
+        byte[] bytes = serialize(obj);
+        return convertBytesToString(bytes);
+    }
+
+    private static byte[] serialize(Object obj) throws IOException {
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        ObjectOutputStream os = new ObjectOutputStream(out);
+        os.writeObject(obj);
+        return out.toByteArray();
+    }
+
+    /**
+     * Check if this string looks like a UUID.
+     *
+     * @param id
+     * @return if the id is formatted correctly
+     */
+    public static boolean isUUID(String id) {
+        // example: "deab5a60-01d3-472f-bc86-4d2bdeb06470"
+        if (id == null || id.length() != 36) {
+            return false;
+        }
+        for (int i = 0; i < id.length(); i++) {
+            char ch = id.charAt(i);
+            switch(i) {
+            case 8:
+            case 13:
+            case 18:
+            case 23:
+                if (ch != '-') {
+                    return false;
+                }
+                break;
+            default:
+                if ((ch < '0' || ch > '9') && (ch < 'a' || ch > 'f') && (ch < 'A' || ch > 'F')) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+}

Propchange: jackrabbit/trunk/contrib/jcrlog/src/main/java/org/apache/jackrabbit/jcrlog/StringUtils.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/trunk/contrib/jcrlog/src/main/java/org/apache/jackrabbit/jcrlog/package.html
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/jcrlog/src/main/java/org/apache/jackrabbit/jcrlog/package.html?view=auto&rev=540521
==============================================================================
--- jackrabbit/trunk/contrib/jcrlog/src/main/java/org/apache/jackrabbit/jcrlog/package.html (added)
+++ jackrabbit/trunk/contrib/jcrlog/src/main/java/org/apache/jackrabbit/jcrlog/package.html Tue May 22 03:26:30 2007
@@ -0,0 +1,41 @@
+<!--
+   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.
+-->
+<body>
+<p>
+Contains the Log Wrapper implementation for the JCR API.
+The Log Wrapper is used to log application method calls to a JCR implementation,
+for example Jackrabbit. The generated log contains all method calls to all
+methods of the JCR API, together with the parameter values used.
+The file is human readable and contains Java source code.
+</p>
+There are two ways to use the Log Wrapper:
+<ul>
+<li>
+	Just after opening the 'real' repository (for example a Jackrabbit repository),
+	wrap it using:<br />
+	{@link org.apache.jackrabbit.jcrlog.RepositoryLogger#wrap(javax.jcr.Repository, java.lang.String) RepositoryLogger.wrap(theRepository, settings)}.
+</li>
+<li>
+	Instantiate the repository using:<br />
+	 RepositoryFactory.open(url).
+	<br />
+	{@link org.apache.jackrabbit.jcrlog.RepositoryFactory#open(java.lang.String) RepositoryFactory.open(url)}.
+</li>
+</ul>
+Afterwards, the repository can be used in the usual way.
+All other classes are not meant to be used by the application directly.
+</body>

Propchange: jackrabbit/trunk/contrib/jcrlog/src/main/java/org/apache/jackrabbit/jcrlog/package.html
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/trunk/contrib/jcrlog/src/main/java/org/apache/jackrabbit/jcrlog/player/Arg.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/jcrlog/src/main/java/org/apache/jackrabbit/jcrlog/player/Arg.java?view=auto&rev=540521
==============================================================================
--- jackrabbit/trunk/contrib/jcrlog/src/main/java/org/apache/jackrabbit/jcrlog/player/Arg.java (added)
+++ jackrabbit/trunk/contrib/jcrlog/src/main/java/org/apache/jackrabbit/jcrlog/player/Arg.java Tue May 22 03:26:30 2007
@@ -0,0 +1,80 @@
+/*
+ * 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.jackrabbit.jcrlog.player;
+
+import org.apache.jackrabbit.jcrlog.StringUtils;
+
+/**
+ * A function call argument used by the statement.
+ *
+ * @author Thomas Mueller
+ *
+ */
+class Arg {
+    private Player player;
+    private Class clazz;
+    private Object obj;
+    private Statement stat;
+
+    Arg(Player player, Class clazz, Object obj) {
+        this.player = player;
+        this.clazz = clazz;
+        this.obj = obj;
+    }
+
+    Arg(Statement stat) {
+        this.stat = stat;
+    }
+
+    public String toString() {
+        if (stat != null) {
+            return stat.toString();
+        } else {
+            return StringUtils.quote(clazz, getValue());
+        }
+    }
+
+    void execute() throws Exception {
+        if (stat != null) {
+            stat.execute();
+            clazz = stat.getReturnClass();
+            obj = stat.getReturnObject();
+            stat = null;
+        }
+    }
+
+    Class getValueClass() {
+        return clazz;
+    }
+
+    Object getValue() {
+        if (obj == null) {
+            return null;
+        } else if (clazz == String.class) {
+            return player.getUUID((String)obj);
+        } else if (clazz == String[].class) {
+            String[] now = (String[])obj;
+            String[] result = new String[now.length];
+            for (int i = 0; i < now.length; i++) {
+                result[i] = player.getUUID(now[i]);
+            }
+            return result;
+        } else {
+            return obj;
+        }
+    }
+}
\ No newline at end of file

Propchange: jackrabbit/trunk/contrib/jcrlog/src/main/java/org/apache/jackrabbit/jcrlog/player/Arg.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/trunk/contrib/jcrlog/src/main/java/org/apache/jackrabbit/jcrlog/player/Parser.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/jcrlog/src/main/java/org/apache/jackrabbit/jcrlog/player/Parser.java?view=auto&rev=540521
==============================================================================
--- jackrabbit/trunk/contrib/jcrlog/src/main/java/org/apache/jackrabbit/jcrlog/player/Parser.java (added)
+++ jackrabbit/trunk/contrib/jcrlog/src/main/java/org/apache/jackrabbit/jcrlog/player/Parser.java Tue May 22 03:26:30 2007
@@ -0,0 +1,312 @@
+/*
+ * 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.jackrabbit.jcrlog.player;
+
+import java.util.ArrayList;
+
+import javax.jcr.Value;
+
+import org.apache.jackrabbit.jcrlog.StringUtils;
+
+/**
+ * The parser to parse a statement (a single line in the log file).
+ *
+ * @author Thomas Mueller
+ *
+ */
+class Parser {
+    private final static int STRING = 0, NAME = 1, NUMBER = 2, SPECIAL = 3;
+
+    private Player player;
+
+    private Statement stat;
+
+    private String line;
+
+    private String token;
+
+    private int tokenType;
+
+    private int pos;
+
+    static Statement parseStatement(Player player, String line) {
+        Parser p = new Parser(player, line);
+        p.parseStatement();
+        return p.stat;
+    }
+
+    private Parser(Player player, String line) {
+        this.player = player;
+        this.line = line;
+        read();
+    }
+
+    private Statement parseStatement() {
+        stat = new Statement(player);
+        String name = readToken();
+        Object o = player.getObject(name);
+        if (o != null) {
+            // n0.getPath()
+            read(".");
+            String methodName = readToken();
+            parseCall(name, o, methodName);
+        } else {
+            if (readIf(".")) {
+                // java.lang.System.exit(0);
+                parseStaticCall(name);
+            } else {
+                // Node n0 = ...
+                if (readIf("[")) {
+                    read("]");
+                }
+                stat.setAssign(name, readToken());
+                read("=");
+                name = readToken();
+                o = player.getObject(name);
+                if (o != null) {
+                    // ... = s0.getRootNode();
+                    read(".");
+                    parseCall(name, o, readToken());
+                } else if (readIf(".")) {
+                    // ... = o.a.RepositoryFactory.open("apache/jackrabbit/transient");
+                    parseStaticCall(name);
+                }
+            }
+        }
+        return stat;
+    }
+
+    private void read() {
+        while (line.charAt(pos) == ' ') {
+            pos++;
+        }
+        int start = pos;
+        char ch = line.charAt(pos);
+        switch (ch) {
+        case '\"':
+            tokenType = STRING;
+            pos++;
+            while (pos < line.length()) {
+                ch = line.charAt(pos);
+                if (ch == '\\') {
+                    pos += 2;
+                } else if (ch == '\"') {
+                    pos++;
+                    break;
+                } else {
+                    pos++;
+                }
+            }
+            break;
+        case '.':
+        case ',':
+        case '(':
+        case ')':
+        case ';':
+        case '{':
+        case '}':
+        case '[':
+        case ']':
+        case '=':
+            tokenType = SPECIAL;
+            pos++;
+            break;
+        default:
+            if (Character.isLetter(ch) || ch == '_') {
+                tokenType = NAME;
+                pos++;
+                while (true) {
+                    ch = line.charAt(pos);
+                    if (Character.isLetterOrDigit(ch) || ch == '_') {
+                        pos++;
+                    } else {
+                        break;
+                    }
+                }
+            } else if (ch == '-' || Character.isDigit(ch)) {
+                tokenType = NUMBER;
+                pos++;
+                while (true) {
+                    ch = line.charAt(pos);
+                    if (Character.isDigit(ch)
+                            || ".+-eElLxabcdefABCDEF".indexOf(ch) >= 0) {
+                        pos++;
+                    } else {
+                        break;
+                    }
+                }
+            }
+        }
+        token = line.substring(start, pos);
+    }
+
+    private boolean readIf(String s) {
+        if (token.equals(s)) {
+            read();
+            return true;
+        }
+        return false;
+    }
+
+    private String readToken() {
+        String s = token;
+        read();
+        return s;
+    }
+
+    private void read(String s) {
+        if (!readIf(s)) {
+            throw new Error("expected: " + s + " read: " + token + " in "
+                    + line);
+        }
+    }
+
+    private Arg parseValue() {
+        if (tokenType == STRING) {
+            String s = readToken();
+            s = StringUtils.javaDecode(s.substring(1, s.length() - 1));
+            return new Arg(player, String.class, s);
+        } else if (tokenType == NUMBER) {
+            String number = readToken().toLowerCase();
+            if (number.startsWith("0x")) {
+                if (number.endsWith("l")) {
+                    Long v = new Long(Long.parseLong(number.substring(2, number
+                            .length() - 1), 16));
+                    return new Arg(player, long.class, v);
+                } else {
+                    Integer v = new Integer(Integer.parseInt(number.substring(
+                            2, number.length() - 1), 16));
+                    return new Arg(player, int.class, v);
+                }
+            } else if (number.indexOf("e") >= 0 || number.indexOf(".") >= 0) {
+                Double v = new Double(Double.parseDouble(number));
+                return new Arg(player, double.class, v);
+            } else if (number.endsWith("l")) {
+                Long v = new Long(Long.parseLong(number.substring(0, number
+                        .length() - 1)));
+                return new Arg(player, long.class, v);
+            } else {
+                Integer v = new Integer(Integer.parseInt(number));
+                return new Arg(player, int.class, v);
+            }
+        } else if (tokenType == NAME) {
+            if (readIf("true")) {
+                return new Arg(player, boolean.class, Boolean.TRUE);
+            } else if (readIf("false")) {
+                return new Arg(player, boolean.class, Boolean.FALSE);
+            } else if (readIf("null")) {
+                throw new Error(
+                        "Null: class not specified. Example: (java.lang.String)null");
+            } else if (readIf("new")) {
+                if (readIf("String")) {
+                    read("[");
+                    read("]");
+                    read("{");
+                    ArrayList values = new ArrayList();
+                    do {
+                        values.add(parseValue().getValue());
+                    } while (readIf(","));
+                    read("}");
+                    String[] list = new String[values.size()];
+                    values.toArray(list);
+                    return new Arg(player, String[].class, list);
+                } else if (readIf("Value")) {
+                    read("[");
+                    read("]");
+                    ArrayList values = new ArrayList();
+                    read("{");
+                    do {
+                        values.add(parseValue());
+                    } while (readIf(","));
+                    read("}");
+                    // TODO Value[] execution isn't implemented yet (but
+                        // should be trivial)
+                    Arg[] list = new Arg[values.size()];
+                    values.toArray(list);
+                    return new Arg(player, Value[].class, list);
+                } else {
+                    // TODO support Version[] versions (Workspace.restore)
+                    throw new Error("Unsupported constructor: " + readToken());
+                }
+            }
+            String name = readToken();
+            Object obj = player.getObject(name);
+            if (obj != null) {
+                return new Arg(player, obj.getClass(), obj);
+            }
+            read(".");
+            Statement outer = stat;
+            stat = new Statement(player);
+            parseStaticCall(name);
+            Arg s = new Arg(stat);
+            stat = outer;
+            return s;
+        } else if (readIf("(")) {
+            String className = readToken();
+            className = parseClassName(className);
+            if (readIf("[")) {
+                read("]");
+            }
+            className = "[L" + className + ";";
+            read(")");
+            read("null");
+            Class c = Player.getClass(className);
+            return new Arg(player, c, null);
+        } else {
+            throw new Error("Expected value, got: " + readToken() + " in "
+                    + line);
+        }
+    }
+
+    private String parseClassName(String clazz) {
+        while (readIf(".")) {
+            clazz += "." + readToken();
+        }
+        return clazz;
+    }
+
+    private void parseCall(String objectName, Object o, String methodName) {
+        stat.setMethodCall(objectName, o, methodName);
+        ArrayList args = new ArrayList();
+        read("(");
+        while (true) {
+            if (readIf(")")) {
+                break;
+            }
+            Arg p = parseValue();
+            args.add(p);
+            if (readIf(")")) {
+                break;
+            }
+            read(",");
+        }
+        stat.setArgs(args);
+    }
+
+    private void parseStaticCall(String clazz) {
+        String last = readToken();
+        // clazz += "." + readToken();
+        while (readIf(".")) {
+            clazz += last == null ? "" : "." + last;
+            last = readToken();
+        }
+        String methodName = last;
+        stat.setStaticCall(clazz);
+        parseCall(null, null, methodName);
+    }
+
+}

Propchange: jackrabbit/trunk/contrib/jcrlog/src/main/java/org/apache/jackrabbit/jcrlog/player/Parser.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/trunk/contrib/jcrlog/src/main/java/org/apache/jackrabbit/jcrlog/player/Player.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/jcrlog/src/main/java/org/apache/jackrabbit/jcrlog/player/Player.java?view=auto&rev=540521
==============================================================================
--- jackrabbit/trunk/contrib/jcrlog/src/main/java/org/apache/jackrabbit/jcrlog/player/Player.java (added)
+++ jackrabbit/trunk/contrib/jcrlog/src/main/java/org/apache/jackrabbit/jcrlog/player/Player.java Tue May 22 03:26:30 2007
@@ -0,0 +1,207 @@
+/*
+ * 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.jackrabbit.jcrlog.player;
+
+import java.io.*;
+import java.util.HashMap;
+
+import org.apache.jackrabbit.jcrlog.LogObject;
+import org.apache.jackrabbit.jcrlog.StringUtils;
+
+/**
+ * The command line tool to re-run the log file. This is done using reflection.
+ *
+ * @author Thomas Mueller
+ *
+ */
+public class Player {
+
+    private boolean log;
+
+    private final static String[] IMPORTED_PACKAGES = { "", "java.lang.",
+            "java.util.", "javax.jcr.", LogObject.MY_PACKAGE_DOT };
+
+    private HashMap objects = new HashMap();
+    private HashMap uuidMap = new HashMap();
+    private String lastUUID;
+    private String lastReturn;
+    private boolean checkResults;
+
+    /**
+     * Execute a trace file using the command line. The log file name to execute (replayed) must be specified
+     * as the last parameter. The following optional command line parameters are supported:
+     * <ul>
+     * <li><code>-log</code> to enable logging the executed statement to System.out
+     * <li><code>-checkResults</code> if this is set, the values returned at runtime are compared with the values in the log file
+     * </ul>
+     *
+     * @param args the arguments of the application
+     */
+    public static void main(String[] args) throws Exception {
+        new Player().run(args);
+    }
+
+    /**
+     * Execute a trace file.
+     *
+     * @param fileName
+     * @param log print debug information
+     * @param checkResult if the result of each method should be compared against the result in the file
+     */
+    public static void execute(String fileName, boolean log, boolean checkResult) throws IOException {
+        new Player().runFile(fileName, log);
+    }
+
+    private void run(String[] args) throws IOException {
+        String fileName;
+        try {
+            fileName = args[args.length - 1];
+            for (int i = 0; i < args.length - 1; i++) {
+                if ("-log".equals(args[i])) {
+                    log = true;
+                } else if ("-checkResults".equals(args[i])) {
+                    checkResults = true;
+                } else {
+                    throw new Error("Unknown setting: " + args[i]);
+                }
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+            System.out.println("Usage: java " + getClass().getName()
+                    + " [-log] [-checkResult] <fileName>");
+            return;
+        }
+        runFile(fileName, log);
+    }
+
+    void addUUID(String oldId, String newId) {
+        if (StringUtils.isUUID(oldId) && StringUtils.isUUID(newId)) {
+            String previous = (String) uuidMap.put(oldId, newId);
+            if (previous != null && !previous.equals(newId)) {
+                log("Duplicate UUID mapping, old:" + oldId + " last:" + previous + " now:" + newId);
+            }
+        }
+    }
+
+    String getUUID(String oldId) {
+        if (StringUtils.isUUID(oldId)) {
+            return (String) uuidMap.get(oldId);
+        }
+        return oldId;
+    }
+
+    private void runFile(String fileName, boolean log) throws IOException {
+        this.log = log;
+        LineNumberReader reader = new LineNumberReader(new BufferedReader(
+                new FileReader(fileName)));
+        while (true) {
+            String line = reader.readLine();
+            if (line == null) {
+                break;
+            }
+            runLine(line.trim());
+        }
+    }
+
+    void log(String s) {
+        if (log) {
+            System.out.println(s);
+        }
+    }
+
+    void logError(String s) {
+        System.out.println("ERROR: " + s);
+    }
+
+    private void runLine(String line) {
+        if (line.startsWith("//return")) {
+            if (line.endsWith("// UUID")) {
+                if (lastUUID == null) {
+                    log("WARNING: no UUID returned");
+                } else {
+                    int start = line.indexOf('\"');
+                    int end = line.lastIndexOf('\"');
+                    if (start < 0 || end <= start) {
+                        log("WARNING: expected return \"...\"");
+                    } else {
+                        String uuid = line.substring(start + 1, end);
+                        addUUID(uuid, lastUUID);
+                        log(">     UUID " + uuid + " -> " + lastUUID);
+                    }
+                }
+            } else if (checkResults && lastReturn != null) {
+                int start = line.indexOf(' ');
+                int end = line.lastIndexOf(';');
+                if (start >= 0 && end > start) {
+                    String expected = line.substring(start, end).trim();
+                    if (lastReturn.equals(expected)) {
+                        logError("expected: " + expected + " got: " + lastReturn);
+                    }
+                }
+            }
+        }
+        if (!line.startsWith("/**/")) {
+            return;
+        }
+        line = line.substring("/**/".length()) + ";";
+        Statement s = Parser.parseStatement(this, line);
+        log("> " + s.toString());
+        try {
+            s.execute();
+            Object result = s.getReturnObject();
+            lastReturn = null;
+            if (result == null) {
+                lastReturn = "null";
+            } else if (result instanceof String) {
+                String r = (String) result;
+                if (StringUtils.isUUID(r)) {
+                    lastUUID = r;
+                    lastReturn = null;
+                } else {
+                    lastUUID = null;
+                    lastReturn = r;
+                }
+            } else {
+                lastReturn = StringUtils.quoteSimple(result);
+            }
+            if (lastReturn != null) {
+                log(">     return " + lastReturn + ";");
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+            log("error: " + e.toString());
+        }
+    }
+
+    static Class getClass(String className) {
+        for (int i = 0; i < IMPORTED_PACKAGES.length; i++) {
+            try {
+                return Class.forName(IMPORTED_PACKAGES[i] + className);
+            } catch (ClassNotFoundException e) {
+            }
+        }
+        throw new Error("Class not found: " + className);
+    }
+
+    void assign(String objectName, Object obj) {
+        objects.put(objectName, obj);
+    }
+
+    Object getObject(String name) {
+        return objects.get(name);
+    }
+}

Propchange: jackrabbit/trunk/contrib/jcrlog/src/main/java/org/apache/jackrabbit/jcrlog/player/Player.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/trunk/contrib/jcrlog/src/main/java/org/apache/jackrabbit/jcrlog/player/Statement.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/jcrlog/src/main/java/org/apache/jackrabbit/jcrlog/player/Statement.java?view=auto&rev=540521
==============================================================================
--- jackrabbit/trunk/contrib/jcrlog/src/main/java/org/apache/jackrabbit/jcrlog/player/Statement.java (added)
+++ jackrabbit/trunk/contrib/jcrlog/src/main/java/org/apache/jackrabbit/jcrlog/player/Statement.java Tue May 22 03:26:30 2007
@@ -0,0 +1,206 @@
+/*
+ * 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.jackrabbit.jcrlog.player;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+
+/**
+ * Represents a statement (a single line in the log file).
+ *
+ * @author Thomas Mueller
+ *
+ */
+class Statement {
+    private Player player;
+
+    private boolean assignment;
+
+    private String assignClass;
+
+    private String assignVariable;
+
+    private boolean staticCall;
+
+    private String staticCallClass;
+
+    private String objectName;
+
+    private Object object;
+
+    private String methodName;
+
+    private Arg[] args;
+
+    private Class[] parameterTypes;
+
+    private Object[] parameters;
+
+    private Method method;
+
+    private Class returnClass;
+
+    private Object returnObject;
+
+    Statement(Player player) {
+        this.player = player;
+    }
+
+    private Method findMethod(Class clazz) throws Exception {
+        if ((clazz.getModifiers() & Modifier.PUBLIC) == 0) {
+            // http://forum.java.sun.com/thread.jspa?threadID=704100&messageID=4084720
+            // bug 4071957
+            Class[] interfaces = clazz.getInterfaces();
+            for (int i = 0; i < interfaces.length; i++) {
+                Class c = interfaces[i];
+                if (c.getName().startsWith("javax.")) {
+                    try {
+                        return c.getMethod(methodName, parameterTypes);
+                    } catch (Exception e) {
+                        // TODO this is slow, but a workaround for a JVM bug
+                    }
+                }
+            }
+        }
+        try {
+            return clazz.getMethod(methodName, parameterTypes);
+        } catch (NoSuchMethodException e) {
+            Method[] methods = clazz.getMethods();
+            methods:
+            for (int i = 0; i < methods.length; i++) {
+                Method m = methods[i];
+                if (methodName.equals(m.getName())) {
+                    Class[] argClasses = m.getParameterTypes();
+                    for (int j = 0; j < args.length; j++) {
+                        if (!argClasses[j].isAssignableFrom(args[j].getValueClass())) {
+                            continue methods;
+                        }
+                    }
+                    return m;
+                }
+            }
+        }
+        throw new Error("Method with args not found: " + clazz.getName() + "."
+                + methodName + " args: " + args.length);
+    }
+
+    void execute() throws Exception {
+        if (object == player) {
+            // there was an exception previously
+            player.log("> " + assignVariable + " not set");
+            if (assignment) {
+                player.assign(assignVariable, player);
+            }
+            return;
+        }
+        Class clazz;
+        if (staticCall) {
+            if (staticCallClass == null || staticCallClass.length() == 0
+                    || !staticCallClass.startsWith("org.")) {
+                player.log("?class? " + staticCallClass);
+            }
+            clazz = Player.getClass(staticCallClass);
+        } else {
+            clazz = object.getClass();
+        }
+        parameterTypes = new Class[args.length];
+        parameters = new Object[args.length];
+        for (int i = 0; i < args.length; i++) {
+            Arg arg = args[i];
+            arg.execute();
+            parameterTypes[i] = arg.getValueClass();
+            parameters[i] = arg.getValue();
+        }
+        method = findMethod(clazz);
+        returnClass = method.getReturnType();
+        try {
+            Object obj = method.invoke(object, parameters);
+            if (assignment) {
+                player.assign(assignVariable, obj);
+            }
+            returnObject = obj;
+        } catch (IllegalArgumentException e) {
+            e.printStackTrace();
+        } catch (IllegalAccessException e) {
+            e.printStackTrace();
+        } catch (InvocationTargetException e) {
+            Throwable t = e.getTargetException();
+            player.log("> " + t.toString());
+            if (assignment) {
+                player.assign(assignVariable, player);
+            }
+        }
+    }
+
+    public String toString() {
+        StringBuffer buff = new StringBuffer();
+        if (assignment) {
+            buff.append(assignClass);
+            buff.append(' ');
+            buff.append(assignVariable);
+            buff.append('=');
+        }
+        if (staticCall) {
+            buff.append(staticCallClass);
+        } else {
+            buff.append(objectName);
+        }
+        buff.append('.');
+        buff.append(methodName);
+        buff.append('(');
+        for (int i = 0; args != null && i < args.length; i++) {
+            if (i > 0) {
+                buff.append(", ");
+            }
+            buff.append(args[i].toString());
+        }
+        buff.append(");");
+        return buff.toString();
+    }
+
+    Class getReturnClass() {
+        return returnClass;
+    }
+
+    Object getReturnObject() {
+        return returnObject;
+    }
+
+    void setAssign(String className, String variableName) {
+        this.assignment = true;
+        this.assignClass = className;
+        this.assignVariable = variableName;
+    }
+
+    void setStaticCall(String className) {
+        this.staticCall = true;
+        this.staticCallClass = className;
+    }
+
+    void setMethodCall(String objectName, Object object, String methodName) {
+        this.objectName = objectName;
+        this.object = object;
+        this.methodName = methodName;
+    }
+
+    public void setArgs(ArrayList list) {
+        args = new Arg[list.size()];
+        list.toArray(args);
+    }
+}

Propchange: jackrabbit/trunk/contrib/jcrlog/src/main/java/org/apache/jackrabbit/jcrlog/player/Statement.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/trunk/contrib/jcrlog/src/main/java/org/apache/jackrabbit/jcrlog/player/package.html
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/jcrlog/src/main/java/org/apache/jackrabbit/jcrlog/player/package.html?view=auto&rev=540521
==============================================================================
--- jackrabbit/trunk/contrib/jcrlog/src/main/java/org/apache/jackrabbit/jcrlog/player/package.html (added)
+++ jackrabbit/trunk/contrib/jcrlog/src/main/java/org/apache/jackrabbit/jcrlog/player/package.html Tue May 22 03:26:30 2007
@@ -0,0 +1,37 @@
+<!--
+   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.
+-->
+
+<body>
+<p>
+This package contains the Log Player to replay recorded log files.
+This is useful for example to reproduce a problem, or to measure the speed of an
+operation.
+</p>
+<p>
+The Player can be started from the command line:
+</p>
+<pre>
+java org.apache.jackrabbit.jcrlog.player.Player log.txt
+</pre>
+<p>
+See also: {@link org.apache.jackrabbit.jcrlog.player.Player Player}.
+</p>
+<p>
+All other classes are not meant to be used by the application directly.
+</p>
+</body>
+

Propchange: jackrabbit/trunk/contrib/jcrlog/src/main/java/org/apache/jackrabbit/jcrlog/player/package.html
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/trunk/contrib/jcrlog/src/test/java/org/apache/jackrabbit/jcrlog/doodle/MyDoodle.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/jcrlog/src/test/java/org/apache/jackrabbit/jcrlog/doodle/MyDoodle.java?view=auto&rev=540521
==============================================================================
--- jackrabbit/trunk/contrib/jcrlog/src/test/java/org/apache/jackrabbit/jcrlog/doodle/MyDoodle.java (added)
+++ jackrabbit/trunk/contrib/jcrlog/src/test/java/org/apache/jackrabbit/jcrlog/doodle/MyDoodle.java Tue May 22 03:26:30 2007
@@ -0,0 +1,58 @@
+/*
+ * 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.jackrabbit.jcrlog.doodle;
+
+import javax.jcr.Repository;
+import javax.jcr.Session;
+
+/**
+ * Scratch pad for transient tests.
+ *
+ * @author Thomas Mueller
+ *
+ */
+public class MyDoodle {
+
+    public static void main(String[] args) throws Exception {
+        new MyDoodle().test();
+    }
+
+    public static void println(String s) {
+        System.out.println(s);
+    }
+
+    public void test() throws Exception {
+
+//      11-10 14:13:28
+//      > org.apache.jackrabbit.jcrlog.samples.FirstHop.main(FirstHop.java:40)
+      /**/Repository rp0 = org.apache.jackrabbit.jcrlog.RepositoryFactory.open("apache/jackrabbit/transient");
+//      > org.apache.jackrabbit.jcrlog.samples.FirstHop.main(FirstHop.java:41)
+      /**/Session s0 = rp0.login();
+//      11-10 14:13:33
+//      time: 4672 ms
+//      > org.apache.jackrabbit.jcrlog.samples.FirstHop.main(FirstHop.java:43)
+      /**/s0.getUserID();
+//      > org.apache.jackrabbit.jcrlog.samples.FirstHop.main(FirstHop.java:44)
+      /**/rp0.getDescriptor("jcr.repository.name");
+//      > org.apache.jackrabbit.jcrlog.samples.FirstHop.main(FirstHop.java:48)
+      /**/s0.logout();
+//      time: 562 ms
+
+
+    }
+
+}

Propchange: jackrabbit/trunk/contrib/jcrlog/src/test/java/org/apache/jackrabbit/jcrlog/doodle/MyDoodle.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/trunk/contrib/jcrlog/src/test/java/org/apache/jackrabbit/jcrlog/doodle/MyItemVisitor.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/jcrlog/src/test/java/org/apache/jackrabbit/jcrlog/doodle/MyItemVisitor.java?view=auto&rev=540521
==============================================================================
--- jackrabbit/trunk/contrib/jcrlog/src/test/java/org/apache/jackrabbit/jcrlog/doodle/MyItemVisitor.java (added)
+++ jackrabbit/trunk/contrib/jcrlog/src/test/java/org/apache/jackrabbit/jcrlog/doodle/MyItemVisitor.java Tue May 22 03:26:30 2007
@@ -0,0 +1,54 @@
+/*
+ * 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.jackrabbit.jcrlog.doodle;
+
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.RepositoryException;
+import javax.jcr.util.TraversingItemVisitor;
+
+import org.apache.jackrabbit.jcrlog.test.unit.TestAPI;
+
+/**
+ * Template item visitor.
+ *
+ * @author Thomas Mueller
+ *
+ */
+public class MyItemVisitor extends TraversingItemVisitor {
+
+    protected void entering(Property property, int level)
+            throws RepositoryException {
+        TestAPI.println("entering property " + property.getString() + " level "
+                + level);
+    }
+
+    protected void entering(Node node, int level) throws RepositoryException {
+        TestAPI.println("entering node " + node.getName() + " level " + level);
+    }
+
+    protected void leaving(Property property, int level)
+            throws RepositoryException {
+        TestAPI.println("leaving property " + property.getString() + " level "
+                + level);
+    }
+
+    protected void leaving(Node node, int level) throws RepositoryException {
+        TestAPI.println("leaving node " + node.getName() + " level " + level);
+    }
+
+}

Propchange: jackrabbit/trunk/contrib/jcrlog/src/test/java/org/apache/jackrabbit/jcrlog/doodle/MyItemVisitor.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/trunk/contrib/jcrlog/src/test/java/org/apache/jackrabbit/jcrlog/samples/FirstHop.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/jcrlog/src/test/java/org/apache/jackrabbit/jcrlog/samples/FirstHop.java?view=auto&rev=540521
==============================================================================
--- jackrabbit/trunk/contrib/jcrlog/src/test/java/org/apache/jackrabbit/jcrlog/samples/FirstHop.java (added)
+++ jackrabbit/trunk/contrib/jcrlog/src/test/java/org/apache/jackrabbit/jcrlog/samples/FirstHop.java Tue May 22 03:26:30 2007
@@ -0,0 +1,52 @@
+/*
+ * 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.jackrabbit.jcrlog.samples;
+
+import org.apache.jackrabbit.jcrlog.RepositoryFactory;
+
+import javax.jcr.Repository;
+import javax.jcr.Session;
+
+/**
+ * Test application from the Jackrabbit homepage.
+ *
+ */
+public class FirstHop {
+
+    /**
+     * The main entry point of the example application.
+     *
+     * @param args command line arguments (ignored)
+     * @throws Exception if an error occurs
+     */
+    public static void main(String[] args) throws Exception {
+        // Repository repository = new TransientRepository();
+        // Repository repository = RepositoryFactory.open("apache/jackrabbit/transient");
+        // Repository repository = RepositoryLogger.wrap(new TransientRepository(), "file=test.txt;sysout=true");
+        Repository repository = RepositoryFactory.open("apache/jackrabbit/logger/file=test.txt;sysout=true;caller=true;url=apache/jackrabbit/transient");
+        Session session = repository.login();
+        try {
+            String user = session.getUserID();
+            String name = repository.getDescriptor(Repository.REP_NAME_DESC);
+            System.out.println(
+                    "Logged in as " + user + " to a " + name + " repository.");
+        } finally {
+            session.logout();
+        }
+    }
+
+}
\ No newline at end of file

Propchange: jackrabbit/trunk/contrib/jcrlog/src/test/java/org/apache/jackrabbit/jcrlog/samples/FirstHop.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/trunk/contrib/jcrlog/src/test/java/org/apache/jackrabbit/jcrlog/samples/SecondHop.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/jcrlog/src/test/java/org/apache/jackrabbit/jcrlog/samples/SecondHop.java?view=auto&rev=540521
==============================================================================
--- jackrabbit/trunk/contrib/jcrlog/src/test/java/org/apache/jackrabbit/jcrlog/samples/SecondHop.java (added)
+++ jackrabbit/trunk/contrib/jcrlog/src/test/java/org/apache/jackrabbit/jcrlog/samples/SecondHop.java Tue May 22 03:26:30 2007
@@ -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.jackrabbit.jcrlog.samples;
+
+import javax.jcr.Node;
+import javax.jcr.Repository;
+import javax.jcr.Session;
+import javax.jcr.SimpleCredentials;
+
+import org.apache.jackrabbit.jcrlog.RepositoryFactory;
+
+/**
+ * Second hop example. Stores, retrieves, and removes example content.
+ */
+public class SecondHop {
+
+    /**
+         * The main entry point of the example application.
+         *
+         * @param args
+         *                command line arguments (ignored)
+         * @throws Exception
+         *                 if an error occurs
+         */
+    public static void main(String[] args) throws Exception {
+        // Repository repository = new TransientRepository();
+        // Repository repository = RepositoryLogger.wrap(new
+        // TransientRepository(), "file=test.txt;sysout=true");
+        Repository repository = RepositoryFactory
+                .open("apache/jackrabbit/logger/file=test.txt;sysout=true;url=apache/jackrabbit/transient");
+        Session session = repository.login(new SimpleCredentials("username",
+                "password".toCharArray()));
+        try {
+            Node root = session.getRootNode();
+
+            // Store content
+            Node hello = root.addNode("hello");
+            Node world = hello.addNode("world");
+            world.setProperty("message", "Hello, World!");
+            session.save();
+
+            // Retrieve content
+            Node node = root.getNode("hello/world");
+            System.out.println(node.getPath());
+            System.out.println(node.getProperty("message").getString());
+
+            // Remove content
+            root.getNode("hello").remove();
+            session.save();
+        } finally {
+            session.logout();
+        }
+    }
+
+}

Propchange: jackrabbit/trunk/contrib/jcrlog/src/test/java/org/apache/jackrabbit/jcrlog/samples/SecondHop.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/trunk/contrib/jcrlog/src/test/java/org/apache/jackrabbit/jcrlog/samples/ThirdHop.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/jcrlog/src/test/java/org/apache/jackrabbit/jcrlog/samples/ThirdHop.java?view=auto&rev=540521
==============================================================================
--- jackrabbit/trunk/contrib/jcrlog/src/test/java/org/apache/jackrabbit/jcrlog/samples/ThirdHop.java (added)
+++ jackrabbit/trunk/contrib/jcrlog/src/test/java/org/apache/jackrabbit/jcrlog/samples/ThirdHop.java Tue May 22 03:26:30 2007
@@ -0,0 +1,110 @@
+/*
+ * 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.jackrabbit.jcrlog.samples;
+
+import java.io.FileInputStream;
+
+import javax.jcr.ImportUUIDBehavior;
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.Property;
+import javax.jcr.PropertyIterator;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.SimpleCredentials;
+import javax.jcr.Value;
+
+import org.apache.jackrabbit.jcrlog.RepositoryFactory;
+
+/**
+ * Third Jackrabbit example application. Imports an example XML file
+ * and outputs the contents of the entire workspace.
+ */
+public class ThirdHop {
+
+    /** Runs the ThirdHop example. */
+    public static void main(String[] args) throws Exception {
+        // Set up a Jackrabbit repository with the specified
+        // configuration file and repository directory
+        // Repository repository = new TransientRepository();
+        // Repository repository = RepositoryLogger.wrap(new TransientRepository(), "file=test.txt;stream=true;sysout=true");
+        Repository repository = RepositoryFactory.open("apache/jackrabbit/logger/file=test.txt;sysout=true;stream=true;url=apache/jackrabbit/transient");
+
+        // Login to the default workspace as a dummy user
+        Session session = repository.login(
+            new SimpleCredentials("username", "password".toCharArray()));
+        try {
+            // Use the root node as a starting point
+            Node root = session.getRootNode();
+
+            // Import the XML file unless already imported
+            if (!root.hasNode("importxml")) {
+                System.out.print("Importing xml... ");
+                // Create an unstructured node under which to import the XML
+                root.addNode("importxml", "nt:unstructured");
+                // Import the file "test.xml" under the created node
+                FileInputStream xml = new FileInputStream("test.xml");
+                session.importXML(
+                    "/importxml", xml, ImportUUIDBehavior.IMPORT_UUID_CREATE_NEW);
+                xml.close();
+                // Save the changes to the repository
+                session.save();
+                System.out.println("done.");
+            }
+
+            dump(root);
+        } finally {
+            session.logout();
+        }
+    }
+
+    /** Recursively outputs the contents of the given node. */
+    private static void dump(Node node) throws RepositoryException {
+        // First output the node path
+        System.out.println(node.getPath());
+        // Skip the virtual (and large!) jcr:system subtree
+        if (node.getName().equals("jcr:system")) {
+            return;
+        }
+
+        // Then output the properties
+        PropertyIterator properties = node.getProperties();
+        while (properties.hasNext()) {
+            Property property = properties.nextProperty();
+            if (property.getDefinition().isMultiple()) {
+                // A multi-valued property, print all values
+                Value[] values = property.getValues();
+                for (int i = 0; i < values.length; i++) {
+                    System.out.println(
+                        property.getPath() + " = " + values[i].getString());
+                }
+            } else {
+                // A single-valued property
+                System.out.println(
+                    property.getPath() + " = " + property.getString());
+            }
+        }
+
+        // Finally output all the child nodes recursively
+        NodeIterator nodes = node.getNodes();
+        while (nodes.hasNext()) {
+            dump(nodes.nextNode());
+        }
+    }
+
+}
\ No newline at end of file

Propchange: jackrabbit/trunk/contrib/jcrlog/src/test/java/org/apache/jackrabbit/jcrlog/samples/ThirdHop.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/trunk/contrib/jcrlog/src/test/java/org/apache/jackrabbit/jcrlog/samples/test.xml
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/jcrlog/src/test/java/org/apache/jackrabbit/jcrlog/samples/test.xml?view=auto&rev=540521
==============================================================================
--- jackrabbit/trunk/contrib/jcrlog/src/test/java/org/apache/jackrabbit/jcrlog/samples/test.xml (added)
+++ jackrabbit/trunk/contrib/jcrlog/src/test/java/org/apache/jackrabbit/jcrlog/samples/test.xml Tue May 22 03:26:30 2007
@@ -0,0 +1,50 @@
+<xhtml:html xmlns:xhtml="http://www.w3.org/1999/xhtml"
+            xmlns:mathml="http://www.w3.org/1998/Math/MathML">
+  <xhtml:head><xhtml:title>Three Namespaces</xhtml:title></xhtml:head>
+  <xhtml:body>
+    <xhtml:h1 align="center">An Ellipse and a Rectangle</xhtml:h1>
+    <svg:svg xmlns:svg="http://www.w3.org/2000/svg" 
+             width="12cm" height="10cm">
+      <svg:ellipse rx="110" ry="130" />
+      <svg:rect x="4cm" y="1cm" width="3cm" height="6cm" />
+    </svg:svg>
+    <xhtml:p>The equation for ellipses</xhtml:p>
+<mathml:math>
+  <mathml:apply>
+    <mathml:eq/>
+    <mathml:cn> 1 </mathml:cn>
+    <mathml:apply>
+      <mathml:plus/>
+      <mathml:apply>
+        <mathml:divide/>
+        <mathml:apply>
+          <mathml:power/>
+          <mathml:ci> x </mathml:ci>
+          <mathml:cn> 2 </mathml:cn>
+        </mathml:apply>
+        <mathml:apply>
+          <mathml:power/>
+          <mathml:ci> a </mathml:ci>
+          <mathml:cn> 2 </mathml:cn>
+        </mathml:apply>
+      </mathml:apply>
+      <mathml:apply>
+        <mathml:divide/>
+        <mathml:apply>
+          <mathml:power/>
+          <mathml:ci> y </mathml:ci>
+          <mathml:cn> 2 </mathml:cn>
+        </mathml:apply>
+        <mathml:apply>
+          <mathml:power/>
+          <mathml:ci> b </mathml:ci>
+          <mathml:cn> 2 </mathml:cn>
+        </mathml:apply>        
+      </mathml:apply>
+    </mathml:apply>
+ </mathml:apply>
+</mathml:math>
+    <xhtml:hr/>
+    <xhtml:p>Last Modified January 10, 2002</xhtml:p>    
+  </xhtml:body>
+</xhtml:html>
\ No newline at end of file

Propchange: jackrabbit/trunk/contrib/jcrlog/src/test/java/org/apache/jackrabbit/jcrlog/samples/test.xml
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/trunk/contrib/jcrlog/src/test/java/org/apache/jackrabbit/jcrlog/test/unit/TestAPI.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/jcrlog/src/test/java/org/apache/jackrabbit/jcrlog/test/unit/TestAPI.java?view=auto&rev=540521
==============================================================================
--- jackrabbit/trunk/contrib/jcrlog/src/test/java/org/apache/jackrabbit/jcrlog/test/unit/TestAPI.java (added)
+++ jackrabbit/trunk/contrib/jcrlog/src/test/java/org/apache/jackrabbit/jcrlog/test/unit/TestAPI.java Tue May 22 03:26:30 2007
@@ -0,0 +1,101 @@
+/*
+ * 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.jackrabbit.jcrlog.test.unit;
+
+import org.apache.jackrabbit.jcrlog.RepositoryFactory;
+import org.apache.jackrabbit.jcrlog.player.Player;
+
+import java.io.IOException;
+
+import javax.jcr.Node;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.SimpleCredentials;
+
+import junit.framework.TestCase;
+
+/**
+ * Simple unit test, checks the basic functionality only.
+ *
+ * @author Thomas Mueller
+ *
+ */
+public class TestAPI extends TestCase {
+
+    public static void main(String[] args) throws Exception {
+        new TestAPI().test();
+    }
+
+    public static void println(String s) {
+        System.out.println(s);
+    }
+
+    public void test() throws Exception {
+        stepCleanRepository();
+        stepRunApp();
+        stepCheckResults();
+        stepCleanRepository();
+        stepReplayLog();
+        stepCheckResults();
+    }
+
+    private Session login(Repository repository) throws RepositoryException {
+        return repository.login(new SimpleCredentials("test", "test"
+                .toCharArray()));
+    }
+
+    private void stepCheckResults() throws RepositoryException {
+        Repository repository = RepositoryFactory
+                .open("apache/jackrabbit/transient");
+        Session session = login(repository);
+        Node root = session.getRootNode();
+        Node test = root.getNode("test");
+        assertNotNull(test);
+        assertEquals("Hello", test.getProperty("name").getString());
+        session.logout();
+    }
+
+    private void stepCleanRepository() throws RepositoryException {
+        Repository repository = RepositoryFactory
+                .open("apache/jackrabbit/transient");
+        Session session = login(repository);
+        Node root = session.getRootNode();
+        if (root.hasNode("test")) {
+            root.getNode("test").remove();
+            session.save();
+        }
+        session.logout();
+    }
+
+    private void stepReplayLog() throws IOException {
+        Player.execute("test.txt", false, true);
+    }
+
+    public void stepRunApp() throws Exception {
+        Repository repository = RepositoryFactory
+                .open("apache/jackrabbit/logger/file=test.txt;url=apache/jackrabbit/transient");
+        // Repository repository =
+        // RepositoryFactory.open("apache/jackrabbit/logger/file=test.txt;sysout=true;url=apache/jackrabbit/transient");
+        Session session = login(repository);
+        Node root = session.getRootNode();
+        Node testNode = root.addNode("test");
+        testNode.setProperty("name", "Hello");
+        session.save();
+        session.logout();
+    }
+}

Propchange: jackrabbit/trunk/contrib/jcrlog/src/test/java/org/apache/jackrabbit/jcrlog/test/unit/TestAPI.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/trunk/contrib/jcrlog/src/test/resources/log4j.properties
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/jcrlog/src/test/resources/log4j.properties?view=auto&rev=540521
==============================================================================
--- jackrabbit/trunk/contrib/jcrlog/src/test/resources/log4j.properties (added)
+++ jackrabbit/trunk/contrib/jcrlog/src/test/resources/log4j.properties Tue May 22 03:26:30 2007
@@ -0,0 +1,3 @@
+log4j.logger.org.apache.jackrabbit=WARN,stdout
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.SimpleLayout
\ No newline at end of file

Propchange: jackrabbit/trunk/contrib/jcrlog/src/test/resources/log4j.properties
------------------------------------------------------------------------------
    svn:eol-style = native