Package org.apache.jackrabbit.core.persistence.bundle.util

Source Code of org.apache.jackrabbit.core.persistence.bundle.util.BundleBinding

/*
* 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: http://svn.apache.org/repos/asf/jackrabbit/tags/1.3.1/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/persistence/bundle/util/BundleBinding.java $ $Rev: 517626 $ $Date: 2007-03-13 12:00:04 +0200 (Tue, 13 Mar 2007) $";

    /**
     * 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[]
            }
        }
    }
}
TOP

Related Classes of org.apache.jackrabbit.core.persistence.bundle.util.BundleBinding

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.