jackrabbit-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From tri...@apache.org
Subject svn commit: r517150 [3/4] - in /jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core: persistence/bundle/ persistence/bundle/util/ state/
Date Mon, 12 Mar 2007 09:42:13 GMT
Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/BundleBinding.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/BundleBinding.java?view=auto&rev=517150
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/BundleBinding.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/BundleBinding.java Mon Mar 12 02:42:12 2007
@@ -0,0 +1,658 @@
+/*
+ * 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.core.persistence.bundle.util;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.jackrabbit.core.persistence.util.BLOBStore;
+import org.apache.jackrabbit.core.persistence.util.ResourceBasedBLOBStore;
+import org.apache.jackrabbit.core.NodeId;
+import org.apache.jackrabbit.core.PropertyId;
+import org.apache.jackrabbit.core.value.InternalValue;
+import org.apache.jackrabbit.core.value.BLOBFileValue;
+import org.apache.jackrabbit.core.nodetype.NodeDefId;
+import org.apache.jackrabbit.core.nodetype.PropDefId;
+import org.apache.jackrabbit.name.QName;
+import org.apache.jackrabbit.uuid.UUID;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import javax.jcr.PropertyType;
+
+/**
+ * This Class implements efficient serialization methods for item states.
+ */
+public class BundleBinding extends ItemStateBinding {
+
+    /** the cvs/svn id */
+    static final String CVS_ID = "$URL$ $Rev$ $Date$";
+
+    /**
+     * default logger
+     */
+    private static Logger log = LoggerFactory.getLogger(BundleBinding.class);
+
+    /**
+     * Creates a new bundle binding
+     *
+     * @param errorHandling the error handling
+     * @param blobStore the blobstore for retrieving blobs
+     * @param nsIndex the namespace index
+     * @param nameIndex the name index
+     */
+    public BundleBinding(ErrorHandling errorHandling, BLOBStore blobStore, StringIndex nsIndex, StringIndex nameIndex) {
+        super(errorHandling, blobStore, nsIndex, nameIndex);
+    }
+
+    /**
+     * Deserializes a <code>NodePropBundle</code> from a data input stream.
+     *
+     * @param in the input stream
+     * @param id the nodeid for the new budle
+     * @return the bundle
+     * @throws IOException if an I/O error occurs.
+     */
+    public NodePropBundle readBundle(DataInputStream in, NodeId id)
+            throws IOException {
+
+        NodePropBundle bundle = new NodePropBundle(id);
+
+        // read version and primary type...special handling
+        int index = in.readInt();
+
+        // get version
+        int version = (index >> 24) & 0xff;
+        index &= 0x00ffffff;
+        String uri = nsIndex.indexToString(index);
+        String local = nameIndex.indexToString(in.readInt());
+        QName nodeTypeName = new QName(uri, local);
+
+        // primaryType
+        bundle.setNodeTypeName(nodeTypeName);
+
+        // parentUUID
+        bundle.setParentId(readID(in));
+
+        // definitionId
+        bundle.setNodeDefId(NodeDefId.valueOf(in.readUTF()));
+
+        // mixin types
+        Set mixinTypeNames = new HashSet();
+        QName name = readIndexedQName(in);
+        while (name != null) {
+            mixinTypeNames.add(name);
+            name = readIndexedQName(in);
+        }
+        bundle.setMixinTypeNames(mixinTypeNames);
+
+        // properties
+        name = readIndexedQName(in);
+        while (name != null) {
+            PropertyId pId = new PropertyId(bundle.getId(), name);
+            NodePropBundle.PropertyEntry pState = readPropertyEntry(in, pId);
+            bundle.addProperty(pState);
+            name = readIndexedQName(in);
+        }
+
+        // set referenceable flag
+        bundle.setReferenceable(in.readBoolean());
+
+        // child nodes (list of uuid/name pairs)
+        NodeId childId = readID(in);
+        while (childId != null) {
+            bundle.addChildNodeEntry(readQName(in), childId);
+            childId = readID(in);
+        }
+
+        // read modcount, since version 1.0
+        if (version >= VERSION_1) {
+            bundle.setModCount(readModCount(in));
+        }
+        return bundle;
+    }
+
+    /**
+     * Checks a <code>NodePropBundle</code> from a data input stream.
+     *
+     * @param in the input stream
+     * @return <code>true</code> if the data is valid;
+     *         <code>false</code> otherwise.
+     */
+    public boolean checkBundle(DataInputStream in) {
+        int version;
+        // primaryType & version
+        try {
+            // read version and primary type...special handling
+            int index = in.readInt();
+
+            // get version
+            version = (index >> 24) & 0xff;
+            index &= 0x00ffffff;
+            String uri = nsIndex.indexToString(index);
+            String local = nameIndex.indexToString(in.readInt());
+            QName nodeTypeName = new QName(uri, local);
+
+            log.info("Serialzation Version: " + version);
+            log.info("NodeTypeName: " + nodeTypeName);
+        } catch (IOException e) {
+            log.error("Error while reading NodeTypeName: " + e);
+            return false;
+        }
+        try {
+            UUID parentUuid = readUUID(in);
+            log.info("ParentUUID: " + parentUuid);
+        } catch (IOException e) {
+            log.error("Error while reading ParentUUID: " + e);
+            return false;
+        }
+        try {
+            String definitionId = in.readUTF();
+            log.info("DefinitionId: " + definitionId);
+        } catch (IOException e) {
+            log.error("Error while reading DefinitionId: " + e);
+            return false;
+        }
+        try {
+            QName mixinName = readIndexedQName(in);
+            while (mixinName != null) {
+                log.info("MixinTypeName: " + mixinName);
+                mixinName = readIndexedQName(in);
+            }
+        } catch (IOException e) {
+            log.error("Error while reading MixinTypes: " + e);
+            return false;
+        }
+        try {
+            QName propName = readIndexedQName(in);
+            while (propName != null) {
+                log.info("PropertyName: " + propName);
+                if (!checkPropertyState(in)) {
+                    return false;
+                }
+                propName = readIndexedQName(in);
+            }
+        } catch (IOException e) {
+            log.error("Error while reading property names: " + e);
+            return false;
+        }
+        try {
+            boolean hasUUID = in.readBoolean();
+            log.info("hasUUID: " + hasUUID);
+        } catch (IOException e) {
+            log.error("Error while reading 'hasUUID': " + e);
+            return false;
+        }
+        try {
+            UUID cneUUID = readUUID(in);
+            while (cneUUID != null) {
+                QName cneName = readQName(in);
+                log.info("ChildNodentry: " + cneUUID + ":" + cneName);
+                cneUUID = readUUID(in);
+            }
+        } catch (IOException e) {
+            log.error("Error while reading child node entry: " + e);
+            return false;
+        }
+
+        if (version >= VERSION_1) {
+            try {
+                short modCount = readModCount(in);
+                log.info("modCount: " + modCount);
+            } catch (IOException e) {
+                log.error("Error while reading mod cout: " + e);
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Serializes a <code>NodePropBundle</code> to a data output stream
+     *
+     * @param out the output stream
+     * @param bundle the bundle to serialize
+     * @throws IOException if an I/O error occurs.
+     */
+    public void writeBundle(DataOutputStream out, NodePropBundle bundle)
+            throws IOException {
+        long size = out.size();
+
+        // primaryType and version
+        out.writeInt((VERSION_CURRENT << 24) | nsIndex.stringToIndex(bundle.getNodeTypeName().getNamespaceURI()));
+        out.writeInt(nameIndex.stringToIndex(bundle.getNodeTypeName().getLocalName()));
+
+        // parentUUID
+        writeID(out, bundle.getParentId());
+
+        // definitionId
+        out.writeUTF(bundle.getNodeDefId().toString());
+
+        // mixin types
+        Iterator iter = bundle.getMixinTypeNames().iterator();
+        while (iter.hasNext()) {
+            writeIndexedQName(out, (QName) iter.next());
+        }
+        writeIndexedQName(out, null);
+
+        // properties
+        iter = bundle.getPropertyNames().iterator();
+        while (iter.hasNext()) {
+            QName pName = (QName) iter.next();
+            NodePropBundle.PropertyEntry pState = bundle.getPropertyEntry(pName);
+            if (pState == null) {
+                log.error("PropertyState missing in bundle: " + pName);
+            } else {
+                writeIndexedQName(out, pName);
+                writeState(out, pState);
+            }
+        }
+        writeIndexedQName(out, null);
+
+        // write uuid flag
+        out.writeBoolean(bundle.isReferenceable());
+
+        // child nodes (list of uuid/name pairs)
+        iter = bundle.getChildNodeEntries().iterator();
+        while (iter.hasNext()) {
+            NodePropBundle.ChildNodeEntry entry = (NodePropBundle.ChildNodeEntry) iter.next();
+            writeID(out, entry.getId());  // uuid
+            writeQName(out, entry.getName());   // name
+        }
+        writeID(out, null);
+
+        // write mod count
+        writeModCount(out, bundle.getModCount());
+
+        // set size of bundle
+        bundle.setSize(out.size()-size);
+    }
+
+    /**
+     * Deserializes a <code>PropertyState</code> from the data input stream.
+     *
+     * @param in the input stream
+     * @param id the property id for the new propert entry
+     * @return the property entry
+     * @throws IOException if an I/O error occurs.
+     */
+    public NodePropBundle.PropertyEntry readPropertyEntry(DataInputStream in, PropertyId id)
+            throws IOException {
+        NodePropBundle.PropertyEntry entry = new NodePropBundle.PropertyEntry(id);
+        // type and modcount
+        int type = in.readInt();
+        entry.setModCount((short) ((type >> 16) & 0x0ffff));
+        type&=0x0ffff;
+        entry.setType(type);
+
+        // multiValued
+        entry.setMultiValued(in.readBoolean());
+        // definitionId
+        entry.setPropDefId(PropDefId.valueOf(in.readUTF()));
+        // values
+        int count = in.readInt();   // count
+        InternalValue[] values = new InternalValue[count];
+        String[] blobIds = new String[count];
+        for (int i = 0; i < count; i++) {
+            InternalValue val;
+            switch (type) {
+                case PropertyType.BINARY:
+                    int size = in.readInt();
+                    if (size == -1) {
+                        blobIds[i] = in.readUTF();
+                        try {
+                            if (blobStore instanceof ResourceBasedBLOBStore) {
+                                val = InternalValue.create(((ResourceBasedBLOBStore) blobStore).getResource(blobIds[i]));
+                            } else {
+                                val = InternalValue.create(blobStore.get(blobIds[i]), false);
+                            }
+                        } catch (IOException e) {
+                            if (errorHandling.ignoreMissingBlobs()) {
+                                log.warn("Ignoring error while reading blob-resource: " + e);
+                                val = InternalValue.create(new byte[0]);
+                            } else {
+                                throw e;
+                            }
+                        } catch (Exception e) {
+                            throw new IOException("Unable to create property value: " + e.toString());
+                        }
+                    } else {
+                        // short values into memory
+                        byte[] data = new byte[size];
+                        in.readFully(data);
+                        val = InternalValue.create(data);
+                    }
+                    break;
+                case PropertyType.DOUBLE:
+                    val = InternalValue.create(in.readDouble());
+                    break;
+                case PropertyType.LONG:
+                    val = InternalValue.create(in.readLong());
+                    break;
+                case PropertyType.BOOLEAN:
+                    val = InternalValue.create(in.readBoolean());
+                    break;
+                case PropertyType.NAME:
+                    val = InternalValue.create(readQName(in));
+                    break;
+                case PropertyType.REFERENCE:
+                    val = InternalValue.create(readUUID(in));
+                    break;
+                default:
+                    // because writeUTF(String) has a size limit of 64k,
+                    // Strings are serialized as <length><byte[]>
+                    int len = in.readInt();
+                    byte[] bytes = new byte[len];
+                    int pos = 0;
+                    while (pos<len) {
+                        pos+= in.read(bytes, pos, len-pos);
+                    }
+                    val = InternalValue.valueOf(new String(bytes, "UTF-8"), type);
+            }
+            values[i] = val;
+        }
+        entry.setValues(values);
+        entry.setBlobIds(blobIds);
+
+        return entry;
+    }
+
+    /**
+     * Checks a <code>PropertyState</code> from the data input stream.
+     *
+     * @param in the input stream
+     * @return <code>true</code> if the data is valid;
+     *         <code>false</code> otherwise.
+     */
+    public boolean checkPropertyState(DataInputStream in) {
+        int type;
+        try {
+            type = in.readInt();
+            short modCount = (short) ((type >> 16) | 0xffff);
+            type &= 0xffff;
+            log.info("  PropertyType: " + PropertyType.nameFromValue(type));
+            log.info("  ModCount: " + modCount);
+        } catch (IOException e) {
+            log.error("Error while reading property type: " + e);
+            return false;
+        }
+        try {
+            boolean isMV = in.readBoolean();
+            log.info("  MultiValued: " + isMV);
+        } catch (IOException e) {
+            log.error("Error while reading multivalued: " + e);
+            return false;
+        }
+        try {
+            String defintionId = in.readUTF();
+            log.info("  DefinitionId: " + defintionId);
+        } catch (IOException e) {
+            log.error("Error while reading definition id: " + e);
+            return false;
+        }
+
+        int count;
+        try {
+            count = in.readInt();
+            log.info("  num values: " + count);
+        } catch (IOException e) {
+            log.error("Error while reading number of values: " + e);
+            return false;
+        }
+        for (int i = 0; i < count; i++) {
+            switch (type) {
+                case PropertyType.BINARY:
+                    int size;
+                    try {
+                        size = in.readInt();
+                        log.info("  binary size: " + size);
+                    } catch (IOException e) {
+                        log.error("Error while reading size of binary: " + e);
+                        return false;
+                    }
+                    if (size == -1) {
+                        try {
+                            String s = in.readUTF();
+                            log.info("  blobid: " + s);
+                        } catch (IOException e) {
+                            log.error("Error while reading blob id: " + e);
+                            return false;
+                        }
+                    } else {
+                        // short values into memory
+                        byte[] data = new byte[size];
+                        try {
+                            in.readFully(data);
+                            log.info("  binary: " + data.length + " bytes");
+                        } catch (IOException e) {
+                            log.error("Error while reading inlined binary: " + e);
+                            return false;
+                        }
+                    }
+                    break;
+                case PropertyType.DOUBLE:
+                    try {
+                        double d = in.readDouble();
+                        log.info("  double: " + d);
+                    } catch (IOException e) {
+                        log.error("Error while reading double value: " + e);
+                        return false;
+                    }
+                    break;
+                case PropertyType.LONG:
+                    try {
+                        double l = in.readLong();
+                        log.info("  long: " + l);
+                    } catch (IOException e) {
+                        log.error("Error while reading long value: " + e);
+                        return false;
+                    }
+                    break;
+                case PropertyType.BOOLEAN:
+                    try {
+                        boolean b = in.readBoolean();
+                        log.info("  boolean: " + b);
+                    } catch (IOException e) {
+                        log.error("Error while reading boolean value: " + e);
+                        return false;
+                    }
+                    break;
+                case PropertyType.NAME:
+                    try {
+                        QName name = readQName(in);
+                        log.info("  name: " + name);
+                    } catch (IOException e) {
+                        log.error("Error while reading name value: " + e);
+                        return false;
+                    }
+                    break;
+                case PropertyType.REFERENCE:
+                    try {
+                        UUID uuid = readUUID(in);
+                        log.info("  reference: " + uuid);
+                    } catch (IOException e) {
+                        log.error("Error while reading reference value: " + e);
+                        return false;
+                    }
+                    break;
+                default:
+                    // because writeUTF(String) has a size limit of 64k,
+                    // Strings are serialized as <length><byte[]>
+                    int len;
+                    try {
+                        len = in.readInt();
+                        log.info("  size of string value: " + len);
+                    } catch (IOException e) {
+                        log.error("Error while reading size of string value: " + e);
+                        return false;
+                    }
+                    try {
+                        byte[] bytes = new byte[len];
+                        int pos = 0;
+                        while (pos<len) {
+                            pos+= in.read(bytes, pos, len-pos);
+                        }
+                        log.info("  string: " + new String(bytes, "UTF-8"));
+                    } catch (IOException e) {
+                        log.error("Error while reading string value: " + e);
+                        return false;
+                    }
+            }
+        }
+        return true;
+    }
+
+
+    /**
+     * Serializes a <code>PropertyState</code> to the data output stream
+     *
+     * @param out the output stream
+     * @param state the property entry to store
+     * @throws IOException if an I/O error occurs.
+     */
+    public void writeState(DataOutputStream out, NodePropBundle.PropertyEntry state)
+            throws IOException {
+        // type & mod count
+        out.writeInt(state.getType() | (state.getModCount() << 16));
+        // multiValued
+        out.writeBoolean(state.isMultiValued());
+        // definitionId
+        out.writeUTF(state.getPropDefId().toString());
+        // values
+        InternalValue[] values = state.getValues();
+        out.writeInt(values.length); // count
+        for (int i = 0; i < values.length; i++) {
+            InternalValue val = values[i];
+            switch (state.getType()) {
+                case PropertyType.BINARY:
+                    // special handling required for binary value:
+                    // spool binary value to file in blob store
+                    BLOBFileValue blobVal = (BLOBFileValue) val.internalValue();
+                    long size = blobVal.getLength();
+                    if (size < 0) {
+                        log.warn("Blob has negative size. Potential loss of data. " +
+                            "id={} idx={}", state.getId(), String.valueOf(i));
+                        out.writeInt(0);
+                        values[i] = InternalValue.create(new byte[0]);
+                        blobVal.discard();
+                    } else if (size > minBlobSize) {
+                        out.writeInt(-1);
+                        String blobId = state.getBlobId(i);
+                        if (blobId == null) {
+                            try {
+                                InputStream in = blobVal.getStream();
+                                try {
+                                    blobId = blobStore.createId(state.getId(), i);
+                                    blobStore.put(blobId, in, size);
+                                    state.setBlobId(blobId, i);
+                                } finally {
+                                    try {
+                                        in.close();
+                                    } catch (IOException e) {
+                                        // ignore
+                                    }
+                                }
+                            } catch (Exception e) {
+                                String msg = "Error while storing blob. id="
+                                        + state.getId() + " idx=" + i + " size=" + size;
+                                log.error(msg, e);
+                                throw new IOException(msg);
+                            }
+                            try {
+                                // replace value instance with value
+                                // backed by resource in blob store and delete temp file
+                                if (blobStore instanceof ResourceBasedBLOBStore) {
+                                    values[i] = InternalValue.create(((ResourceBasedBLOBStore) blobStore).getResource(blobId));
+                                } else {
+                                    values[i] = InternalValue.create(blobStore.get(blobId), false);
+                                }
+                            } catch (Exception e) {
+                                log.error("Error while reloading blob. truncating. id=" + state.getId() +
+                                        " idx=" + i + " size=" + size, e);
+                                values[i] = InternalValue.create(new byte[0]);
+                            }
+                            blobVal.discard();
+                        }
+                        // store id of blob as property value
+                        out.writeUTF(blobId);   // value
+                    } else {
+                        // delete evt. blob
+                        out.writeInt((int) size);
+                        byte[] data = new byte[(int) size];
+                        try {
+                            InputStream in = blobVal.getStream();
+                            try {
+                                int pos = 0;
+                                while (pos < size) {
+                                    int n = in.read(data, pos, (int) size-pos);
+                                    if (n < 0) {
+                                        throw new EOFException();
+                                    }
+                                    pos += n;
+                                }
+                            } finally {
+                                try {
+                                    in.close();
+                                } catch (IOException e) {
+                                    // ignore
+                                }
+                            }
+                        } catch (Exception e) {
+                            String msg = "Error while storing blob. id="
+                                    + state.getId() + " idx=" + i + " size=" + size;
+                            log.error(msg, e);
+                            throw new IOException(msg);
+                        }
+                        out.write(data, 0, data.length);
+                        // replace value instance with value
+                        // backed by resource in blob store and delete temp file
+                        values[i] = InternalValue.create(data);
+                        blobVal.discard();
+                    }
+                    break;
+                case PropertyType.DOUBLE:
+                    out.writeDouble(((Double) val.internalValue()).doubleValue());
+                    break;
+                case PropertyType.LONG:
+                    out.writeLong(((Long) val.internalValue()).longValue());
+                    break;
+                case PropertyType.BOOLEAN:
+                    out.writeBoolean(((Boolean) val.internalValue()).booleanValue());
+                    break;
+                case PropertyType.NAME:
+                    writeQName(out, (QName) val.internalValue());
+                    break;
+                case PropertyType.REFERENCE:
+                    writeUUID(out, (UUID) val.internalValue());
+                    break;
+                default:
+                    // because writeUTF(String) has a size limit of 64k,
+                    // we're using write(byte[]) instead
+                    byte[] bytes = val.toString().getBytes("UTF-8");
+                    out.writeInt(bytes.length); // lenght of byte[]
+                    out.write(bytes);   // byte[]
+            }
+        }
+    }
+}

Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/BundleBinding.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/BundleBinding.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url rev

Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/BundleCache.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/BundleCache.java?view=auto&rev=517150
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/BundleCache.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/BundleCache.java Mon Mar 12 02:42:12 2007
@@ -0,0 +1,205 @@
+/*
+ * 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.core.persistence.bundle.util;
+
+import org.apache.commons.collections.map.LinkedMap;
+import org.apache.jackrabbit.core.NodeId;
+import org.slf4j.LoggerFactory;
+import org.slf4j.Logger;
+
+/**
+ * This Class implements a simple cache for nodeprop bundles
+ */
+public class BundleCache {
+
+    /** the cvs/svn id */
+    static final String CVS_ID = "$URL$ $Rev$ $Date$";
+
+    /**
+     * the default logger
+     */
+    private static Logger log = LoggerFactory.getLogger(BundleCache.class);
+
+    /**
+     * the current memory usage of this cache
+     */
+    private long curSize = 0;
+
+    /**
+     * the maximum chache size
+     */
+    private long maxSize;
+
+    /**
+     * the number of cache hits
+     */
+    private long hits = 0;
+
+    /**
+     * the number of cache misses
+     */
+    private long misses = 0;
+
+    /**
+     * a map of the cache entries
+     */
+    private LinkedMap bundles = new LinkedMap();
+
+    /**
+     * Creates a new BundleCache
+     *
+     * @param maxSize the maximum size of this cache in bytes.
+     */
+    public BundleCache(long maxSize) {
+        this.maxSize = maxSize;
+    }
+
+    /**
+     * Returns the maximum cache size in bytes.
+     *
+     * @return the maximum cache size in bytes.
+     */
+    public long getMaxSize() {
+        return maxSize;
+    }
+
+    /**
+     * Sets the maximum cache size in bytes.
+     *
+     * @param maxSize the maximum cache size in bytes.
+     */
+    public void setMaxSize(long maxSize) {
+        this.maxSize = maxSize;
+    }
+
+    /**
+     * Returns the bundle with the given <code>id</code> or <code>null</code>
+     * if the bundle is not cached.
+     *
+     * @param id the id of the bundle
+     * @return the cached bundle or <code>null</code>
+     */
+    public NodePropBundle get(NodeId id) {
+        Entry entry = (Entry) bundles.remove(id);
+        if (entry != null) {
+            // at end
+            bundles.put(id, entry);
+            hits++;
+        } else {
+            misses++;
+        }
+        if (log.isInfoEnabled() && (hits+misses)%10000 == 0) {
+            long c = curSize/1024;
+            long m = maxSize/1024;
+            long a = bundles.size()>0 ? curSize/bundles.size() : 0;
+            log.info("num=" + bundles.size() + " mem=" + c + "k max=" + m + "k avg=" + a
+                    + " hits=" + hits + " miss=" + misses);
+        }
+        return entry == null ? null : entry.bundle;
+    }
+
+    /**
+     * Puts a bunlde to the cache. If the new size of the cache exceeds the
+     * {@link #getMaxSize() max size} of the cache it will remove bundles from
+     * this cache until the limit is satisfied.
+     *
+     * @param bundle the bunlde to put to the cache
+     */
+    public void put(NodePropBundle bundle) {
+        Entry entry = (Entry) bundles.remove(bundle.getId());
+        if (entry == null) {
+            entry = new Entry(bundle, bundle.getSize());
+        } else {
+            curSize -= entry.size;
+            entry.bundle = bundle;
+            entry.size = bundle.getSize();
+        }
+        bundles.put(bundle.getId(), entry);
+        curSize+= entry.size;
+        // now limit size of cache
+        while (curSize > maxSize) {
+            entry = (Entry) bundles.remove(0);
+            curSize-= entry.size;
+        }
+    }
+
+    /**
+     * Checks if the bundle with the given id is cached.
+     *
+     * @param id the id of the bundle
+     * @return <code>true</code> if the bundle is cached;
+     *         <code>false</code> otherwise.
+     */
+    public boolean contains(NodeId id) {
+        return bundles.containsKey(id);
+    }
+
+    /**
+     * Removes a bundle from this cache.
+     *
+     * @param id the id of the bunlde to remove.
+     * @return the previously cached bunlde or <code>null</code> of the bundle
+     *         was not cached.
+     */
+    public NodePropBundle remove(NodeId id) {
+        Entry entry = (Entry) bundles.remove(id);
+        if (entry != null) {
+            curSize-= entry.size;
+            return entry.bundle;
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Clears this cache and removes all bundles.
+     */
+    public void clear() {
+        bundles.clear();
+        curSize=0;
+        hits=0;
+        misses=0;
+    }
+
+    /**
+     * Internal class that holds the bundles.
+     */
+    private static final class Entry {
+
+        /**
+         * the cached bundle
+         */
+        private NodePropBundle bundle;
+
+        /**
+         * the memory usage of the bundle in bytes
+         */
+        private long size;
+
+        /**
+         * Creates a new entry.
+         *
+         * @param bundle the bundle to cache
+         * @param size the size of the bundle
+         */
+        public Entry(NodePropBundle bundle, long size) {
+            this.bundle = bundle;
+            this.size = size;
+        }
+    }
+
+}

Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/BundleCache.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/BundleCache.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url rev

Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/DbNameIndex.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/DbNameIndex.java?view=auto&rev=517150
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/DbNameIndex.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/DbNameIndex.java Mon Mar 12 02:42:12 2007
@@ -0,0 +1,243 @@
+/*
+ * 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.core.persistence.bundle.util;
+
+import java.util.HashMap;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+/**
+ * Implements a {@link StringIndex} that stores and retrieves the names from a
+ * table in a database.
+ *
+ * Note that this class is not threadsafe by itself. it needs to be synchronized
+ * by the using application.
+ */
+public class DbNameIndex implements StringIndex {
+
+    /**
+     * The CVS/SVN id
+     */
+    static final String CVS_ID = "$URL$ $Rev$ $Date$";
+
+    // name index statements
+    protected PreparedStatement nameSelect;
+    protected PreparedStatement indexSelect;
+    protected PreparedStatement nameInsert;
+
+    // caches
+    private final HashMap string2Index = new HashMap();
+    private final HashMap index2String= new HashMap();
+
+    /**
+     * Creates a new index that is stored in a db.
+     * @param con the jdbc connection
+     * @param schemaObjectPrefix the prefix for table names
+     * @throws SQLException if the statements cannot be prepared.
+     */
+    public DbNameIndex(Connection con, String schemaObjectPrefix)
+            throws SQLException {
+        init(con, schemaObjectPrefix);
+    }
+
+    /**
+     * Inits this index and prepares the statements.
+     *
+     * @param con the jdbc connection
+     * @param schemaObjectPrefix the prefix for table names
+     * @throws SQLException if the statements cannot be prepared.
+     */
+    protected void init(Connection con, String schemaObjectPrefix)
+            throws SQLException {
+        nameSelect = con.prepareStatement("select NAME from " + schemaObjectPrefix + "NAMES where ID = ?");
+        indexSelect = con.prepareStatement("select ID from " + schemaObjectPrefix + "NAMES where NAME = ?");
+        nameInsert = con.prepareStatement("insert into " + schemaObjectPrefix + "NAMES (NAME) values (?)", Statement.RETURN_GENERATED_KEYS);
+    }
+
+    /**
+     * Closes this index and releases it's resources.
+     */
+    public void close() {
+        closeStatement(nameSelect);
+        closeStatement(indexSelect);
+        closeStatement(nameInsert);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public int stringToIndex(String string) {
+        // check cache
+        Integer index = (Integer) string2Index.get(string);
+        if (index == null) {
+            int idx = getIndex(string);
+            if (idx == -1) {
+                idx = insertString(string);
+            }
+            index = new Integer(idx);
+            string2Index.put(string, index);
+            index2String.put(index, string);
+            return idx;
+        } else {
+            return index.intValue();
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String indexToString(int idx) {
+        // check cache
+        Integer index = new Integer(idx);
+        String s = (String) index2String.get(index);
+        if (s == null) {
+            s = getString(idx);
+            if (s == null) {
+                throw new IllegalStateException("String empty???");
+            }
+            index2String.put(index, s);
+            string2Index.put(s, index);
+        }
+        return s;
+    }
+
+    /**
+     * Inserts a string into the database and returns the new index.
+     *
+     * @param string the string to insert
+     * @return the new index.
+     */
+    protected int insertString(String string) {
+        // assert index does not exist
+        PreparedStatement stmt = nameInsert;
+        ResultSet rs = null;
+        try {
+            stmt.setString(1, string);
+            stmt.executeUpdate();
+            rs = stmt.getGeneratedKeys();
+            if (!rs.next()) {
+                return -1;
+            } else {
+                return rs.getInt(1);
+            }
+        } catch (Exception e) {
+            throw new IllegalStateException("Unable to insert index: " + e);
+        } finally {
+            closeResultSet(rs);
+            resetStatement(stmt);
+        }
+    }
+
+    /**
+     * Retrieves the index from the database for the given string.
+     * @param string the string to retrieve the index for
+     * @return the index or -1 if not found.
+     */
+    protected int getIndex(String string) {
+        PreparedStatement stmt = indexSelect;
+        ResultSet rs = null;
+        try {
+            stmt.setString(1, string);
+            stmt.execute();
+            rs = stmt.getResultSet();
+            if (!rs.next()) {
+                return -1;
+            } else {
+                return rs.getInt(1);
+            }
+        } catch (Exception e) {
+            throw new IllegalStateException("Unable to read index: " + e);
+        } finally {
+            closeResultSet(rs);
+            resetStatement(stmt);
+        }
+    }
+
+    /**
+     * Retrieves the string from the database for the givein index.
+     * @param index the index to retrieve the string for.
+     * @return the string or <code>null</code> if not found.
+     */
+    private String getString(int index) {
+        PreparedStatement stmt = nameSelect;
+        ResultSet rs = null;
+        try {
+            stmt.setInt(1, index);
+            stmt.execute();
+            rs = stmt.getResultSet();
+            if (!rs.next()) {
+                return null;
+            } else {
+                return rs.getString(1);
+            }
+        } catch (Exception e) {
+            throw new IllegalStateException("Unable to read name: " + e);
+        } finally {
+            closeResultSet(rs);
+            resetStatement(stmt);
+        }
+    }
+
+    /**
+     * closes the statement
+     * @param stmt the statement
+     */
+    protected void closeStatement(PreparedStatement stmt) {
+        if (stmt != null) {
+            try {
+                stmt.close();
+            } catch (SQLException se) {
+                // ignore
+            }
+        }
+    }
+    /**
+     * Resets the given <code>PreparedStatement</code> by clearing the
+     * parameters and warnings contained.
+     *
+     * @param stmt The <code>PreparedStatement</code> to reset. If
+     *             <code>null</code> this method does nothing.
+     */
+    protected void resetStatement(PreparedStatement stmt) {
+        if (stmt != null) {
+            try {
+                stmt.clearParameters();
+                stmt.clearWarnings();
+            } catch (SQLException se) {
+                // ignore
+            }
+        }
+    }
+
+    /**
+     * Closes the result set
+     * @param rs the result set.
+     */
+    protected void closeResultSet(ResultSet rs) {
+        if (rs != null) {
+            try {
+                rs.close();
+            } catch (SQLException se) {
+                // ignore
+            }
+        }
+    }
+}
\ No newline at end of file

Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/DbNameIndex.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/DbNameIndex.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url rev

Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/ErrorHandling.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/ErrorHandling.java?view=auto&rev=517150
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/ErrorHandling.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/ErrorHandling.java Mon Mar 12 02:42:12 2007
@@ -0,0 +1,83 @@
+/*
+ * 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.core.persistence.bundle.util;
+
+import java.util.HashSet;
+import java.util.Iterator;
+
+/**
+ * ErrorHandling configuration abstraction class
+ */
+public final class ErrorHandling {
+
+    /**
+     * Controls if references to missing blob resources are treaded as errors
+     * or not.
+     */
+    public static final String IGNORE_MISSING_BLOBS = "IGN_MISSING_BLOBS";
+
+    /** all available configuration codes */
+    private static final String[] CODES = {
+            IGNORE_MISSING_BLOBS
+    };
+
+    /** the flags */
+    private final HashSet flags = new HashSet();
+
+    /**
+     * Creates a default error handling config.
+     */
+    public ErrorHandling() {
+    }
+
+    /**
+     * Creates a new error handling configuration based on the given string.
+     * The individual flags should be seperated with "|".
+     *
+     * @param str flags
+     */
+    public ErrorHandling(String str) {
+        for (int i=0; i<CODES.length; i++) {
+            if (str.indexOf(CODES[i])>=0) {
+                flags.add(CODES[i]);
+            }
+        }
+    }
+
+    /**
+     * Checks if error handling is set to ignore missing blobs
+     * @return <code>true</code> if error handling is set to ignore missing blobs.
+     */
+    public boolean ignoreMissingBlobs() {
+        return flags.contains(IGNORE_MISSING_BLOBS);
+    }
+
+    /**
+     * Returns the string representation where the flags are seperated
+     * with "|".
+     * @return the string representation.
+     */
+    public String toString() {
+        StringBuffer ret = new StringBuffer("|");
+        Iterator iter = flags.iterator();
+        while (iter.hasNext()) {
+            ret.append(iter.next());
+        }
+        ret.append("|");
+        return ret.toString();
+    }
+}
\ No newline at end of file

Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/ErrorHandling.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/ErrorHandling.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url rev

Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/HashMapIndex.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/HashMapIndex.java?view=auto&rev=517150
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/HashMapIndex.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/HashMapIndex.java Mon Mar 12 02:42:12 2007
@@ -0,0 +1,174 @@
+/*
+ * 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.core.persistence.bundle.util;
+
+import java.util.HashMap;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Iterator;
+import java.util.Properties;
+
+import org.apache.jackrabbit.core.fs.FileSystemException;
+import org.apache.jackrabbit.core.fs.FileSystemResource;
+
+/**
+ * Implements a {@link StringIndex} that is based on a hashmap and persists
+ * the names as property file.
+ * <p/>
+ * Please note that this class is not synchronized and the calls need to ensure
+ * thread safeness.
+ */
+public class HashMapIndex implements StringIndex {
+
+    /** the cvs/svn id */
+    static final String CVS_ID = "$URL$ $Rev$ $Date$";
+
+    /**
+     * holds the string-to-index lookups.
+     */
+    private final HashMap stringToIndex = new HashMap();
+
+    /**
+     * holds the index-to-string lookups.
+     */
+    private final HashMap indexToString = new HashMap();
+
+    /**
+     * a copy of the {@link #stringToIndex} as properties class for faster
+     * storing.
+     */
+    private final Properties stringToIndexProps = new Properties();
+
+    /**
+     * the filesystem resource that stores the lookup tables.
+     */
+    private FileSystemResource file;
+
+    /**
+     * the time when the resource was last modified.
+     */
+    private long lastModified = -1;
+
+    /**
+     * Creates a new hashmap index and loads the lookup tables from the
+     * filesystem resource. If it does not exist yet, it will create a new one.
+     *
+     * @param file the filesystem resource that stores the lookup tables.
+     *
+     * @throws IOException if an I/O error occurs.
+     * @throws FileSystemException if an I/O error occurs.
+     */
+    public HashMapIndex(FileSystemResource file)
+            throws FileSystemException, IOException {
+        this.file = file;
+        if (!file.exists()) {
+            file.makeParentDirs();
+            file.getOutputStream().close();
+        }
+        load();
+    }
+
+    /**
+     * Loads the lookup table from the filesystem resource.
+     *
+     * @throws IOException if an I/O error occurs.
+     * @throws FileSystemException if an I/O error occurs.
+     */
+    private void load() throws IOException, FileSystemException {
+        long modTime = file.lastModified();
+        if (modTime > lastModified) {
+            InputStream in = file.getInputStream();
+            stringToIndexProps.clear();
+            stringToIndexProps.load(in);
+            Iterator iter = stringToIndexProps.keySet().iterator();
+            while (iter.hasNext()) {
+                String uri = (String) iter.next();
+                String prop = stringToIndexProps.getProperty(uri);
+                Integer idx = new Integer(prop);
+                stringToIndex.put(uri, idx);
+                indexToString.put(idx, uri);
+            }
+            in.close();
+        }
+        lastModified = modTime;
+    }
+
+    /**
+     * Saves the lookup table to the filesystem resource.
+     *
+     * @throws IOException if an I/O error occurs.
+     * @throws FileSystemException if an I/O error occurs.
+     */
+    private void save() throws IOException, FileSystemException {
+        OutputStream out = file.getOutputStream();
+        stringToIndexProps.store(out, "string index");
+        out.close();
+        lastModified = file.lastModified();
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * This implementation reloads the table from the resource if a lookup fails
+     * and if the resource was modified since.
+     */
+    public int stringToIndex(String nsUri) {
+        Integer idx = (Integer) stringToIndex.get(nsUri);
+        if (idx == null) {
+            try {
+                load();
+            } catch (Exception e) {
+                throw new IllegalStateException("Unable to load lookup table: " + e);
+            }
+            idx = (Integer) stringToIndex.get(nsUri);
+        }
+        if (idx == null) {
+            idx = new Integer(indexToString.size());
+            stringToIndex.put(nsUri, idx);
+            indexToString.put(idx, nsUri);
+            stringToIndexProps.put(nsUri, idx.toString());
+            try {
+                save();
+            } catch (Exception e) {
+                throw new IllegalStateException("Unable to store lookup table: "  +e);
+            }
+        }
+        return idx.intValue();
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * This implementation reloads the table from the resource if a lookup fails
+     * and if the resource was modified since.
+     */
+    public String indexToString(int i) {
+        Integer idx = new Integer(i);
+        String s = (String) indexToString.get(idx);
+        if (s == null) {
+            try {
+                load();
+            } catch (Exception e) {
+                throw new IllegalStateException("Unable to load lookup table: " + e);
+            }
+            s = (String) indexToString.get(idx);
+        }
+        return s;
+    }
+}

Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/HashMapIndex.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/HashMapIndex.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url rev

Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/ItemStateBinding.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/ItemStateBinding.java?view=auto&rev=517150
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/ItemStateBinding.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/ItemStateBinding.java Mon Mar 12 02:42:12 2007
@@ -0,0 +1,643 @@
+/*
+ * 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.core.persistence.bundle.util;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.jackrabbit.core.persistence.util.BLOBStore;
+import org.apache.jackrabbit.core.persistence.util.ResourceBasedBLOBStore;
+import org.apache.jackrabbit.core.persistence.PersistenceManager;
+import org.apache.jackrabbit.core.state.NodeReferencesId;
+import org.apache.jackrabbit.core.state.NodeReferences;
+import org.apache.jackrabbit.core.state.NodeState;
+import org.apache.jackrabbit.core.state.PropertyState;
+import org.apache.jackrabbit.core.PropertyId;
+import org.apache.jackrabbit.core.NodeId;
+import org.apache.jackrabbit.core.value.InternalValue;
+import org.apache.jackrabbit.core.value.BLOBFileValue;
+import org.apache.jackrabbit.core.nodetype.NodeDefId;
+import org.apache.jackrabbit.core.nodetype.PropDefId;
+import org.apache.jackrabbit.name.QName;
+import org.apache.jackrabbit.uuid.UUID;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import javax.jcr.PropertyType;
+
+/**
+ * This Class implements relatively efficient serialization methods for item
+ * states.
+ */
+public class ItemStateBinding {
+
+    /** the cvs/svn id */
+    static final String CVS_ID = "$URL$ $Rev$ $Date$";
+
+    /**
+     * default logger
+     */
+    private static Logger log = LoggerFactory.getLogger(ItemStateBinding.class);
+
+    /**
+     * serialization version 1
+     */
+    public final static int VERSION_1 = 1;
+
+    /**
+     * current version
+     */
+    public final static int VERSION_CURRENT = VERSION_1;
+
+    /**
+     * the namespace index
+     */
+    protected final StringIndex nsIndex;
+
+    /**
+     * the name index
+     */
+    protected final StringIndex nameIndex;
+
+    /**
+     * the blob store
+     */
+    protected final BLOBStore blobStore;
+
+    /**
+     * minimum size of binaries to store in blob store
+     */
+    protected long minBlobSize = 0x4000; // 16k
+
+    /**
+     * the error handling
+     */
+    protected final ErrorHandling errorHandling;
+
+    /**
+     * Creates a new item state binding
+     *
+     * @param errorHandling the error handing configuration
+     * @param blobStore the blobstore
+     * @param nsIndex the namespace index
+     * @param nameIndex the name index
+     */
+    public ItemStateBinding(ErrorHandling errorHandling,
+                            BLOBStore blobStore,
+                            StringIndex nsIndex, StringIndex nameIndex) {
+        this.errorHandling = errorHandling;
+        this.nsIndex = nsIndex;
+        this.nameIndex = nameIndex;
+        this.blobStore = blobStore;
+    }
+
+    /**
+     * Returns the minimum blob size
+     * @see #setMinBlobSize(long) for details.
+     * @return the minimum blob size
+     */
+    public long getMinBlobSize() {
+        return minBlobSize;
+    }
+
+    /**
+     * Sets the minimum blob size. Binary values that are shorted than this given
+     * size will be inlined in the serialization stream, binary value that are
+     * longer, will be stored in the blob store. default is 4k.
+     *
+     * @param minBlobSize the minimum blob size.
+     */
+    public void setMinBlobSize(long minBlobSize) {
+        this.minBlobSize = minBlobSize;
+    }
+
+    /**
+     * Deserializes a <code>NodeReferences</code> from the data input stream.
+     *
+     * @param in the input stream
+     * @param id the id of the nodereference to deserialize
+     * @param pMgr the persistence manager
+     * @return the node references
+     * @throws IOException in an I/O error occurs.
+     */
+    public NodeReferences readState(DataInputStream in, NodeReferencesId id,
+                                    PersistenceManager pMgr)
+            throws IOException {
+        NodeReferences state = new NodeReferences(id);
+        int count = in.readInt();   // count & version
+        int version = (count >> 24) | 0x0ff;
+        count &= 0x00ffffff;
+        for (int i = 0; i < count; i++) {
+            state.addReference(readPropertyId(in));    // propertyId
+        }
+        return state;
+    }
+
+    /**
+     * Serializes a <code>NodeReferences</code> to the data output stream.
+     *
+     * @param out the output stream
+     * @param state the state to write.
+     * @throws IOException in an I/O error occurs.
+     */
+    public void writeState(DataOutputStream out, NodeReferences state)
+            throws IOException {
+        // references
+        Collection c = state.getReferences();
+        out.writeInt(c.size() | (VERSION_CURRENT << 24)); // count
+        for (Iterator iter = c.iterator(); iter.hasNext();) {
+            PropertyId propId = (PropertyId) iter.next();
+            writePropertyId(out, propId);
+        }
+    }
+
+    /**
+     * Deserializes a <code>NodeState</code> from the data input stream.
+     *
+     * @param in the input streaam
+     * @param id the id of the nodestate to read
+     * @param pMgr the persistence manager
+     * @return the node state
+     * @throws IOException in an I/O error occurs.
+     */
+    public NodeState readState(DataInputStream in, NodeId id,
+                               PersistenceManager pMgr)
+            throws IOException {
+        NodeState state = pMgr.createNew(id);
+        // primaryType & version
+        int index = in.readInt();
+        int version = (index >> 24) & 0x0ff;
+        String uri = nsIndex.indexToString(index & 0x0ffffff);
+        String local = in.readUTF();
+        state.setNodeTypeName(new QName(uri, local));
+
+        // parentUUID
+        state.setParentId(readID(in));
+        // definitionId
+        state.setDefinitionId(NodeDefId.valueOf(in.readUTF()));
+
+        // mixin types
+        int count = in.readInt();   // count
+        Set set = new HashSet(count);
+        for (int i = 0; i < count; i++) {
+            set.add(readQName(in)); // name
+        }
+        if (set.size() > 0) {
+            state.setMixinTypeNames(set);
+        }
+        // properties (names)
+        count = in.readInt();   // count
+        for (int i = 0; i < count; i++) {
+            state.addPropertyName(readIndexedQName(in)); // name
+        }
+        // child nodes (list of name/uuid pairs)
+        count = in.readInt();   // count
+        for (int i = 0; i < count; i++) {
+            QName name = readQName(in);
+            NodeId parentId = readID(in);
+            state.addChildNodeEntry(name, parentId);
+        }
+
+        if (version >= VERSION_1) {
+            state.setModCount(readModCount(in));
+        }
+        return state;
+    }
+
+    /**
+     * Serializes a <code>NodeState</code> to the data output stream
+     *
+     * @param out the output stream
+     * @param state the state to write
+     * @throws IOException in an I/O error occurs.
+     */
+    public void writeState(DataOutputStream out, NodeState state)
+            throws IOException {
+        // primaryType & version
+        out.writeInt((VERSION_CURRENT << 24) | nsIndex.stringToIndex(state.getNodeTypeName().getNamespaceURI()));
+        out.writeUTF(state.getNodeTypeName().getLocalName());
+        // parentUUID
+        writeID(out, state.getParentId());
+        // definitionId
+        out.writeUTF(state.getDefinitionId().toString());
+        // mixin types
+        Collection c = state.getMixinTypeNames();
+        out.writeInt(c.size()); // count
+        for (Iterator iter = c.iterator(); iter.hasNext();) {
+            writeQName(out, (QName) iter.next());
+        }
+        // properties (names)
+        c = state.getPropertyNames();
+        out.writeInt(c.size()); // count
+        for (Iterator iter = c.iterator(); iter.hasNext();) {
+            QName pName = (QName) iter.next();
+            writeIndexedQName(out, pName);
+        }
+        // child nodes (list of name/uuid pairs)
+        c = state.getChildNodeEntries();
+        out.writeInt(c.size()); // count
+        for (Iterator iter = c.iterator(); iter.hasNext();) {
+            NodeState.ChildNodeEntry entry = (NodeState.ChildNodeEntry) iter.next();
+            writeQName(out, entry.getName());   // name
+            writeID(out, entry.getId());  // uuid
+        }
+        writeModCount(out, state.getModCount());
+    }
+
+    /**
+     * Deserializes a <code>PropertyState</code> from the data input stream.
+     *
+     * @param in the input stream
+     * @param id the id for the new property state
+     * @param pMgr the persistence manager
+     * @return the property state
+     * @throws IOException in an I/O error occurs.
+     */
+    public PropertyState readState(DataInputStream in, PropertyId id,
+                                   PersistenceManager pMgr)
+            throws IOException {
+        PropertyState state = pMgr.createNew(id);
+        // type and modcount
+        int type = in.readInt();
+        state.setModCount((short) ((type >> 16) & 0x0ffff));
+        type&=0x0ffff;
+        state.setType(type);
+        // multiValued
+        state.setMultiValued(in.readBoolean());
+        // definitionId
+        state.setDefinitionId(PropDefId.valueOf(in.readUTF()));
+        // values
+        int count = in.readInt();   // count
+        InternalValue[] values = new InternalValue[count];
+        for (int i = 0; i < count; i++) {
+            InternalValue val;
+            switch (type) {
+                case PropertyType.BINARY:
+                    int size = in.readInt();
+                    if (size == -1) {
+                        String s = in.readUTF();
+                        try {
+                            if (blobStore instanceof ResourceBasedBLOBStore) {
+                                val = InternalValue.create(
+                                        ((ResourceBasedBLOBStore) blobStore).getResource(s));
+                            } else {
+                                val = InternalValue.create(blobStore.get(s), false);
+                            }
+                        } catch (IOException e) {
+                            if (errorHandling.ignoreMissingBlobs()) {
+                                log.warn("Ignoring error while reading blob-resource: " + e);
+                                val = InternalValue.create(new byte[0]);
+                            } else {
+                                throw e;
+                            }
+                        } catch (Exception e) {
+                            throw new IOException("Unable to create property value: " + e.toString());
+                        }
+                    } else {
+                        // short values into memory
+                        byte[] data = new byte[size];
+                        in.readFully(data);
+                        val = InternalValue.create(data);
+                    }
+                    break;
+                case PropertyType.DOUBLE:
+                    val = InternalValue.create(in.readDouble());
+                    break;
+                case PropertyType.LONG:
+                    val = InternalValue.create(in.readLong());
+                    break;
+                case PropertyType.BOOLEAN:
+                    val = InternalValue.create(in.readBoolean());
+                    break;
+                case PropertyType.NAME:
+                    val = InternalValue.create(readQName(in));
+                    break;
+                case PropertyType.REFERENCE:
+                    val = InternalValue.create(readUUID(in));
+                    break;
+                default:
+                    // because writeUTF(String) has a size limit of 64k,
+                    // Strings are serialized as <length><byte[]>
+                    int len = in.readInt();
+                    byte[] bytes = new byte[len];
+                    int pos=0;
+                    while (pos < len) {
+                        pos+= in.read(bytes, pos, len-pos);
+                    }
+                    val = InternalValue.valueOf(new String(bytes, "UTF-8"), type);
+            }
+            values[i] = val;
+        }
+        state.setValues(values);
+        return state;
+    }
+
+    /**
+     * Serializes a <code>PropertyState</code> to the data output stream
+     * @param out the output stream
+     * @param state the property state to write
+     * @throws IOException in an I/O error occurs.
+     */
+    public void writeState(DataOutputStream out, PropertyState state)
+            throws IOException {
+        // type & mod count
+        out.writeInt(state.getType() | (state.getModCount() << 16));
+        // multiValued
+        out.writeBoolean(state.isMultiValued());
+        // definitionId
+        out.writeUTF(state.getDefinitionId().toString());
+        // values
+        InternalValue[] values = state.getValues();
+        out.writeInt(values.length); // count
+        for (int i = 0; i < values.length; i++) {
+            InternalValue val = values[i];
+            switch (state.getType()) {
+                case PropertyType.BINARY:
+                    try {
+                        // special handling required for binary value:
+                        // spool binary value to file in blob store
+                        BLOBFileValue blobVal = (BLOBFileValue) val.internalValue();
+                        long size = blobVal.getLength();
+                        if (size > minBlobSize) {
+                            out.writeInt(-1);
+                            InputStream in = blobVal.getStream();
+                            String blobId = blobStore.createId((PropertyId) state.getId(), i);
+                            try {
+                                blobStore.put(blobId, in, size);
+                            } finally {
+                                try {
+                                    in.close();
+                                } catch (IOException e) {
+                                    // ignore
+                                }
+                            }
+                            // store id of blob as property value
+                            out.writeUTF(blobId);   // value
+                            // replace value instance with value
+                            // backed by resource in blob store and delete temp file
+                            if (blobStore instanceof ResourceBasedBLOBStore) {
+                                values[i] = InternalValue.create(((ResourceBasedBLOBStore) blobStore).getResource(blobId));
+                            } else {
+                                values[i] = InternalValue.create(blobStore.get(blobId), false);
+                            }
+                            blobVal.discard();
+                        } else {
+                            // delete evt. blob
+                            out.writeInt((int) size);
+                            byte[] data = new byte[(int) size];
+                            InputStream in = blobVal.getStream();
+                            try {
+                                int pos = 0;
+                                while (pos < size) {
+                                    int n = in.read(data, pos, (int) size-pos);
+                                    if (n < 0) {
+                                        throw new EOFException();
+                                    }
+                                    pos += n;
+                                }
+                            } finally {
+                                try {
+                                    in.close();
+                                } catch (IOException e) {
+                                    // ignore
+                                }
+                            }
+                            out.write(data, 0, data.length);
+                            // replace value instance with value
+                            // backed by resource in blob store and delete temp file
+                            values[i] = InternalValue.create(data);
+                            blobVal.discard();
+                        }
+                    } catch (IOException e) {
+                        throw e;
+                    } catch (Exception e) {
+                        throw new IOException("Error converting: " + e.toString());
+                    }
+                    break;
+                case PropertyType.DOUBLE:
+                    out.writeDouble(((Double) val.internalValue()).doubleValue());
+                    break;
+                case PropertyType.LONG:
+                    out.writeLong(((Long) val.internalValue()).longValue());
+                    break;
+                case PropertyType.BOOLEAN:
+                    out.writeBoolean(((Boolean) val.internalValue()).booleanValue());
+                    break;
+                case PropertyType.NAME:
+                    writeQName(out, (QName) val.internalValue());
+                    break;
+                case PropertyType.REFERENCE:
+                    writeUUID(out, (UUID) val.internalValue());
+                    break;
+                default:
+                    // because writeUTF(String) has a size limit of 64k,
+                    // we're using write(byte[]) instead
+                    byte[] bytes = val.toString().getBytes("UTF-8");
+                    out.writeInt(bytes.length); // lenght of byte[]
+                    out.write(bytes);   // byte[]
+            }
+        }
+    }
+
+    /**
+     * Deserializes a UUID
+     * @param in the input stream
+     * @return the uuid
+     * @throws IOException in an I/O error occurs.
+     */
+    public UUID readUUID(DataInputStream in) throws IOException {
+        if (in.readBoolean()) {
+            byte[] bytes = new byte[16];
+            int pos=0;
+            while (pos < 16) {
+                pos += in.read(bytes, pos, 16-pos);
+            }
+            return new UUID(bytes);
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Serializes a UUID
+     * @param out the output stream
+     * @param uuid the uuid
+     * @throws IOException in an I/O error occurs.
+     */
+    public void writeUUID(DataOutputStream out, String uuid) throws IOException {
+        if (uuid == null) {
+            out.writeBoolean(false);
+        } else {
+            out.writeBoolean(true);
+            out.write(UUID.fromString(uuid).getRawBytes());
+        }
+    }
+
+    /**
+     * Deserializes a NodeID
+     * @param in the input stream
+     * @return the uuid
+     * @throws IOException in an I/O error occurs.
+     */
+    public NodeId readID(DataInputStream in) throws IOException {
+        if (in.readBoolean()) {
+            byte[] bytes = new byte[16];
+            int pos=0;
+            while (pos < 16) {
+                pos += in.read(bytes, pos, 16-pos);
+            }
+            return new NodeId(new UUID(bytes));
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Serializes a node id
+     * @param out the output stream
+     * @param id the id
+     * @throws IOException in an I/O error occurs.
+     */
+    public void writeID(DataOutputStream out, NodeId id) throws IOException {
+        if (id == null) {
+            out.writeBoolean(false);
+        } else {
+            out.writeBoolean(true);
+            out.write(id.getUUID().getRawBytes());
+        }
+    }
+
+    /**
+     * Serializes a UUID
+     * @param out the output stream
+     * @param uuid the uuid
+     * @throws IOException in an I/O error occurs.
+     */
+    public void writeUUID(DataOutputStream out, UUID uuid) throws IOException {
+        if (uuid == null) {
+            out.writeBoolean(false);
+        } else {
+            out.writeBoolean(true);
+            out.write(uuid.getRawBytes());
+        }
+    }
+
+    /**
+     * Deserializes a QName
+     * @param in the input stream
+     * @return the qname
+     * @throws IOException in an I/O error occurs.
+     */
+    public QName readQName(DataInputStream in) throws IOException {
+        String uri = nsIndex.indexToString(in.readInt());
+        String local = in.readUTF();
+        return new QName(uri, local);
+    }
+
+    /**
+     * Deserializes a mod-count
+     * @param in the input stream
+     * @return the mod count
+     * @throws IOException in an I/O error occurs.
+     */
+    public short readModCount(DataInputStream in) throws IOException {
+        return in.readShort();
+    }
+
+    /**
+     * Sersializes a mod-count
+     * @param out the output stream
+     * @param modCount the mod count
+     * @throws IOException in an I/O error occurs.
+     */
+    public void writeModCount(DataOutputStream out, short modCount) throws IOException {
+        out.writeShort(modCount);
+    }
+
+    /**
+     * Serializes a QName
+     * @param out the output stream
+     * @param name the name
+     * @throws IOException in an I/O error occurs.
+     */
+    public void writeQName(DataOutputStream out, QName name) throws IOException {
+        out.writeInt(nsIndex.stringToIndex(name.getNamespaceURI()));
+        out.writeUTF(name.getLocalName());
+    }
+
+    /**
+     * Deserializes an indexed QName
+     * @param in the input stream
+     * @return the qname
+     * @throws IOException in an I/O error occurs.
+     */
+    public QName readIndexedQName(DataInputStream in) throws IOException {
+        int index = in.readInt();
+        if (index<0) {
+            return null;
+        } else {
+            String uri = nsIndex.indexToString(index);
+            String local = nameIndex.indexToString(in.readInt());
+            return new QName(uri, local);
+        }
+    }
+
+    /**
+     * Serializes a indexed QName
+     * @param out the output stream
+     * @param name the name
+     * @throws IOException in an I/O error occurs.
+     */
+    public void writeIndexedQName(DataOutputStream out, QName name) throws IOException {
+        if (name == null) {
+            out.writeInt(-1);
+        } else {
+            out.writeInt(nsIndex.stringToIndex(name.getNamespaceURI()));
+            out.writeInt(nameIndex.stringToIndex(name.getLocalName()));
+        }
+    }
+
+    /**
+     * Serializes a PropertyId
+     * @param out the output stream
+     * @param id the id
+     * @throws IOException in an I/O error occurs.
+     */
+    public void writePropertyId(DataOutputStream out, PropertyId id) throws IOException {
+        writeID(out, id.getParentId());
+        writeQName(out, id.getName());
+    }
+
+    /**
+     * Deserializes a PropertyId
+     * @param in the input stream
+     * @return the property id
+     * @throws IOException in an I/O error occurs.
+     */
+    public PropertyId readPropertyId(DataInputStream in) throws IOException {
+        UUID uuid = readUUID(in);
+        QName name = readQName(in);
+        return new PropertyId(new NodeId(uuid), name);
+    }
+}

Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/ItemStateBinding.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/ItemStateBinding.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url rev

Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/LRUNodeIdCache.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/LRUNodeIdCache.java?view=auto&rev=517150
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/LRUNodeIdCache.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/LRUNodeIdCache.java Mon Mar 12 02:42:12 2007
@@ -0,0 +1,109 @@
+/*
+ * 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.core.persistence.bundle.util;
+
+import org.apache.commons.collections.map.LinkedMap;
+import org.apache.jackrabbit.core.NodeId;
+import org.slf4j.LoggerFactory;
+import org.slf4j.Logger;
+
+/**
+ * Implements a LRU NodeId cache.
+ */
+public class LRUNodeIdCache {
+
+    /**
+     * the cvs/svn id
+     */
+    static final String CVS_ID = "$URL$ $Rev$ $Date$";
+
+    /**
+     * The default logger
+     */
+    private static Logger log = LoggerFactory.getLogger(LRUNodeIdCache.class);
+
+    /**
+     * The maximum number of ids to cache
+     */
+    private long maxSize = 10240;
+
+    /**
+     * the number of cache hits
+     */
+    private long hits = 0;
+
+    /**
+     * the number of cache misses
+     */
+    private long misses = 0;
+
+    /**
+     * the map of cached ids
+     */
+    private final LinkedMap missing = new LinkedMap();
+
+    /**
+     * Checks if the given id is contained in this cached.
+     *
+     * @param id the id to check
+     * @return <code>true</code> if the id is cached;
+     *         <code>false</code> otherwise.
+     */
+    public boolean contains(NodeId id) {
+        Object o = missing.remove(id);
+        if (o == null) {
+            misses++;
+        } else {
+            missing.put(id, id);
+            hits++;
+        }
+        if (log.isInfoEnabled() && (hits+misses)%10000 == 0) {
+            log.info("num=" + missing.size() + "/" + maxSize + " hits=" + hits + " miss=" + misses);
+        }
+        return o != null;
+    }
+
+    /**
+     * Puts the given id to this cache.
+     * @param id the id to put.
+     */
+    public void put(NodeId id) {
+        if (!missing.containsKey(id)) {
+            if (missing.size() == maxSize) {
+                missing.remove(0);
+            }
+            missing.put(id, id);
+        }
+    }
+
+    /**
+     * Removes the it to this cache
+     * @param id the id to remove
+     * @return <code>true</code> if the id was cached;
+     *         <code>false</code> otherwise.
+     */
+    public boolean remove(NodeId id) {
+        return missing.remove(id) != null;
+    }
+
+    /**
+     * Clears this cache.
+     */
+    public void clear() {
+        missing.clear();
+    }
+}
\ No newline at end of file

Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/LRUNodeIdCache.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/LRUNodeIdCache.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url rev

Added: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/NGKDbNameIndex.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/NGKDbNameIndex.java?view=auto&rev=517150
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/NGKDbNameIndex.java (added)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/NGKDbNameIndex.java Mon Mar 12 02:42:12 2007
@@ -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.jackrabbit.core.persistence.bundle.util;
+
+import org.apache.jackrabbit.core.persistence.bundle.util.DbNameIndex;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.Statement;
+
+/**
+ * Same as {@link DbNameIndex} but does not make use of the
+ * {@link Statement#RETURN_GENERATED_KEYS} feature as it might not be provided
+ * by the underlying database (e.g. oracle).
+ */
+public class NGKDbNameIndex extends DbNameIndex {
+
+    /**
+     * The CVS/SVN id
+     */
+    static final String CVS_ID = "$URL$ $Rev$ $Date$";
+
+    /**
+     * Creates a new index that is stored in a db.
+     * @param con the jdbc connection
+     * @param schemaObjectPrefix the prefix for table names
+     * @throws SQLException if the statements cannot be prepared.
+     */
+    public NGKDbNameIndex(Connection con, String schemaObjectPrefix)
+            throws SQLException {
+        super(con, schemaObjectPrefix);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected void init(Connection con, String schemaObjectPrefix)
+            throws SQLException {
+        nameSelect = con.prepareStatement("select NAME from " + schemaObjectPrefix + "NAMES where ID = ?");
+        indexSelect = con.prepareStatement("select ID from " + schemaObjectPrefix + "NAMES where NAME = ?");
+        nameInsert = con.prepareStatement("insert into " + schemaObjectPrefix + "NAMES (NAME) values (?)");
+    }
+
+    /**
+     * Inserts a string into the database and returns the new index.
+     * <p/>
+     * Instead of using the {@link Statement#RETURN_GENERATED_KEYS} feature, the
+     * newly inserted index is retrieved by a 2nd select statement.
+     *
+     * @param string the string to insert
+     * @return the new index.
+     */
+    protected int insertString(String string) {
+        // assert index does not exist
+        PreparedStatement stmt = nameInsert;
+        ResultSet rs = null;
+        try {
+            stmt.setString(1, string);
+            stmt.executeUpdate();
+            return getIndex(string);
+        } catch (Exception e) {
+            throw new IllegalStateException("Unable to insert index: " + e);
+        } finally {
+            closeResultSet(rs);
+            resetStatement(stmt);
+        }
+    }
+}
\ No newline at end of file

Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/NGKDbNameIndex.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/NGKDbNameIndex.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url rev



Mime
View raw message