com.silverwrist.dynamo.unistore.BinaryPartImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.silverwrist.dynamo.unistore.BinaryPartImpl.java

Source

/*
 * The contents of this file are subject to the Mozilla Public License Version 1.1
 * (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.mozilla.org/MPL/>.
 * 
 * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT
 * WARRANTY OF ANY KIND, either express or implied. See the License for the specific
 * language governing rights and limitations under the License.
 * 
 * The Original Code is the Venice Web Communities System.
 * 
 * The Initial Developer of the Original Code is Eric J. Bowersox <erbo@silcom.com>,
 * for Silverwrist Design Studios.  Portions created by Eric J. Bowersox are
 * Copyright (C) 2003 Eric J. Bowersox/Silverwrist Design Studios.  All Rights Reserved.
 * 
 * Contributor(s): 
 */
package com.silverwrist.dynamo.unistore;

import java.io.*;
import java.sql.Blob;
import java.sql.SQLException;
import java.util.*;
import org.apache.commons.collections.*;
import com.silverwrist.dynamo.Namespaces;
import com.silverwrist.dynamo.db.NamespaceCache;
import com.silverwrist.dynamo.event.*;
import com.silverwrist.dynamo.except.*;
import com.silverwrist.dynamo.iface.*;
import com.silverwrist.dynamo.util.*;

class BinaryPartImpl implements UniStoreBinaryPart {
    /*--------------------------------------------------------------------------------
     * Internal class implementing a Blob return
     *--------------------------------------------------------------------------------
     */

    private class ReturnBlob implements Blob {
        /*====================================================================
         * Attributes
         *====================================================================
         */

        private byte[] m_data; // the actual blob data

        /*====================================================================
         * Constructor
         *====================================================================
         */

        ReturnBlob(byte[] data) {
            m_data = data;

        } // end constructor

        /*====================================================================
         * Implementations from interface Blob
         *====================================================================
         */

        public long length() {
            return m_size;

        } // end length

        public byte[] getBytes(long pos, int length) throws SQLException {
            if ((pos < 1) || (pos > (long) m_size))
                throw new SQLException("invalid position value");
            if ((length <= 0) || ((pos + length - 1) > (long) m_size))
                throw new SQLException("invalid length value");
            byte[] rc = new byte[length];
            System.arraycopy(m_data, (int) pos, rc, 0, length);
            return rc;

        } // end getBytes

        public InputStream getBinaryStream() {
            return new ByteArrayInputStream(m_data);

        } // end getBinaryStream

        public long position(byte[] pattern, long start) throws SQLException {
            throw new SQLException("position() method not implemented");

        } // end position

        public long position(Blob pattern, long start) throws SQLException {
            throw new SQLException("position() method not implemented");

        } // end position

        public int setBytes(long pos, byte[] bytes) throws SQLException {
            throw new SQLException("setBytes() method not implemented");

        } // end setBytes

        public int setBytes(long pos, byte[] bytes, int offset, int len) throws SQLException {
            throw new SQLException("setBytes() method not implemented");

        } // end setBytes

        public OutputStream setBinaryStream(long pos) throws SQLException {
            throw new SQLException("setBinaryStream() method not implemented");

        } // end setBinaryStream

        public void truncate(long len) throws SQLException {
            throw new SQLException("truncate() method not implemented");

        } // end truncate

    } // end class ReturnBlob

    /*--------------------------------------------------------------------------------
     * Attributes
     *--------------------------------------------------------------------------------
     */

    private BinaryPartOps m_ops;
    private NamespaceCache m_nscache;
    private PostDynamicUpdate m_post;
    private MessageImpl m_parent;
    private int m_part;
    private QualifiedNameKey m_identity;
    private String m_mimetype;
    private int m_size;
    private String m_filename;
    private int m_nread;
    private java.util.Date m_lastread;
    private ReferenceMap m_properties;

    /*--------------------------------------------------------------------------------
     * Constructors
     *--------------------------------------------------------------------------------
     */

    BinaryPartImpl(BinaryPartOps ops, NamespaceCache nscache, PostDynamicUpdate post, MessageImpl parent,
            Map params) throws DatabaseException {
        m_ops = ops;
        m_nscache = nscache;
        m_post = post;
        m_parent = parent;
        m_part = ((Integer) (params.get(MessageOps.PARAM_PART))).intValue();
        PropertyKey pk = (PropertyKey) (params.get(MessageOps.PARAM_IDENTITY));
        m_identity = new QualifiedNameKey(nscache.namespaceIdToName(pk.getNamespaceID()), pk.getName());
        m_mimetype = (String) (params.get(MessageOps.PARAM_MIMETYPE));
        Integer tmp = (Integer) (params.get(MessageOps.PARAM_SIZE));
        if (tmp != null)
            m_size = tmp.intValue();
        m_filename = (String) (params.get(MessageOps.PARAM_FILENAME));
        m_nread = ((Integer) (params.get(MessageOps.PARAM_READS))).intValue();
        m_lastread = (java.util.Date) (params.get(MessageOps.PARAM_LASTREAD));
        m_properties = new ReferenceMap(ReferenceMap.HARD, ReferenceMap.SOFT);

    } // end constructor

    BinaryPartImpl(PostDynamicUpdate post, MessageImpl parent, int part, QualifiedNameKey identity) {
        m_ops = null;
        m_nscache = null;
        m_post = post;
        m_parent = parent;
        m_part = part;
        m_identity = identity;
        m_mimetype = null;
        m_size = -1;
        m_filename = null;
        m_nread = 0;
        m_lastread = null;
        m_properties = new ReferenceMap(ReferenceMap.HARD, ReferenceMap.SOFT);

    } // end constructor

    /*--------------------------------------------------------------------------------
     * Overrides from class Object
     *--------------------------------------------------------------------------------
     */

    public String toString() {
        if (m_ops == null)
            return "(deleted binary part)";
        return "message " + m_parent.getMessageID() + ", binary part " + m_identity.toString();

    } // end toString

    /*--------------------------------------------------------------------------------
     * Implementations from interface ObjectProvider
     *--------------------------------------------------------------------------------
     */

    /**
     * Retrieves an object from this <CODE>ObjectProvider</CODE>.
     *
     * @param namespace The namespace to interpret the name relative to.
     * @param name The name of the object to be retrieved.
     * @return The object reference specified.
     */
    public Object getObject(String namespace, String name) {
        if (m_ops == null)
            throw new NoSuchObjectException(this.toString(), namespace, name);
        try { // convert the namespace name to an ID here
            PropertyKey key = new PropertyKey(m_nscache.namespaceNameToId(namespace), name);
            Object rc = null;
            synchronized (this) { // start by looking in the properties map
                rc = m_properties.get(key);
                if (rc == null) { // no use - need to try the database
                    rc = m_ops.getProperty(m_parent.getMessageID(), m_part, key);
                    if (rc != null)
                        m_properties.put(key, rc);

                } // end if

            } // end synchronized block

            if (rc == null)
                throw new NoSuchObjectException(this.toString(), namespace, name);
            return rc;

        } // end try
        catch (DatabaseException e) { // translate into our NoSuchObjectException but retain the DatabaseException
            throw new NoSuchObjectException(this.toString(), namespace, name, e);

        } // end catch

    } // end getObject

    /*--------------------------------------------------------------------------------
     * Implementations from interface SecureObjectStore
     *--------------------------------------------------------------------------------
     */

    /**
     * Sets an object into this <CODE>SecureObjectStore</CODE>.
     *
     * @param caller The user performing the operation.
     * @param namespace The namespace to interpret the name relative to.
     * @param name The name of the object to be set.
     * @param value The object to set into the <CODE>SecureObjectStore</CODE>.
     * @return The previous object that was set into the <CODE>SecureObjectStore</CODE> under this namespace and name, or
     *         <CODE>null</CODE> if there was no such object.
     * @exception com.silverwrist.dynamo.except.DatabaseException If there was an error setting the object value.
     * @exception com.silverwrist.dynamo.except.DynamoSecurityException If the specified user is not permitted to
     *            set this object value into this <CODE>SecureObjectStore</CODE>.
     */
    public Object setObject(DynamoUser caller, String namespace, String name, Object value)
            throws DatabaseException, DynamoSecurityException {
        if (m_ops == null)
            throw new DatabaseException(BinaryPartImpl.class, "UniStoreMessages", "part.deleted");
        m_parent.testPermission(caller, namespace, "set.property", "no.setProperty");
        Object rc = null;
        // convert the namespace name to an ID here
        PropertyKey key = new PropertyKey(m_nscache.namespaceNameToId(namespace), name);
        synchronized (this) { // start by setting the database value
            rc = m_ops.setProperty(m_parent.getMessageID(), m_part, key, value);

            // and cache it, too
            m_properties.put(key, value);

        } // end synchronized block

        m_post.postUpdate(new MessagePartPropertyUpdateEvent(this, namespace, name));
        return rc;

    } // end setObject

    /**
     * Removes an object from this <CODE>SecureObjectStore</CODE>.
     *
     * @param caller The user performing the operation.
     * @param namespace The namespace to interpret the name relative to.
     * @param name The name of the object to be removed.
     * @return The previous object that was set into the <CODE>SecureObjectStore</CODE> under this namespace and name, or
     *         <CODE>null</CODE> if there was no such object.
     * @exception com.silverwrist.dynamo.except.DatabaseException If there was an error removing the object value.
     * @exception com.silverwrist.dynamo.except.DynamoSecurityException If the specified user is not permitted to
     *            remove this object value from this <CODE>SecureObjectStore</CODE>.
     */
    public Object removeObject(DynamoUser caller, String namespace, String name)
            throws DatabaseException, DynamoSecurityException {
        if (m_ops == null)
            throw new DatabaseException(BinaryPartImpl.class, "UniStoreMessages", "part.deleted");
        m_parent.testPermission(caller, namespace, "remove.property", "no.removeProperty");
        Object rc = null;
        // convert the namespace name to an ID here
        PropertyKey key = new PropertyKey(m_nscache.namespaceNameToId(namespace), name);
        synchronized (this) { // start by killing the database value
            rc = m_ops.removeProperty(m_parent.getMessageID(), m_part, key);

            // and remove the cached value, too
            m_properties.remove(key);

        } // end synchronized block

        m_post.postUpdate(new MessagePartPropertyUpdateEvent(this, namespace, name));
        return rc;

    } // end removeObject

    /**
     * Returns a collection of all object namespaces that have been set into this <CODE>SecureObjectStore</CODE>.
     *
     * @return A {@link java.util.Collection Collection} containing {@link java.lang.String String} objects specifying
     *         all the object namespaces.
     * @exception com.silverwrist.dynamo.except.DatabaseException If there was an error getting the namespace list.
     */
    public Collection getNamespaces() throws DatabaseException {
        if (m_ops == null)
            return Collections.EMPTY_LIST;

        // call through to the database to get the list of namespace IDs
        int[] ids = m_ops.getPropertyNamespaceIDs(m_parent.getMessageID(), m_part);

        ArrayList rc = new ArrayList(ids.length);
        for (int i = 0; i < ids.length; i++)
            rc.add(m_nscache.namespaceIdToName(ids[i]));
        return Collections.unmodifiableList(rc);

    } // end getNamespaces

    /**
     * Returns a collection of all object names that have been set into this <CODE>SecureObjectStore</CODE> under
     * a given namespace.
     *
     * @param namespace The namespace to look for names under.
     * @return A {@link java.util.Collection Collection} containing {@link java.lang.String String} objects
     *         specifying all the object names for this namespace.
     * @exception com.silverwrist.dynamo.except.DatabaseException If there was an error getting the object name list.
     */
    public Collection getNamesForNamespace(String namespace) throws DatabaseException {
        if (m_ops == null)
            return Collections.EMPTY_LIST;

        // call through to the database to get the data for this namespace
        int nsid = m_nscache.namespaceNameToId(namespace);
        Map data = m_ops.getAllProperties(m_parent.getMessageID(), m_part, nsid);

        // we both create the return value and cache the data values
        ArrayList rc = new ArrayList(data.size());
        synchronized (this) { // do the transfer...
            Iterator it = data.entrySet().iterator();
            while (it.hasNext()) { // copy one entry at a time
                Map.Entry ntry = (Map.Entry) (it.next());
                rc.add(ntry.getKey().toString());
                m_properties.put(new PropertyKey(nsid, ntry.getKey().toString()), ntry.getValue());

            } // end while

        } // end synchronized block

        return Collections.unmodifiableList(rc);

    } // end getNamesForNamespace

    /*--------------------------------------------------------------------------------
     * Implementations from interface UniStorePart
     *--------------------------------------------------------------------------------
     */

    public long getMessageID() {
        return (m_parent == null) ? -1L : m_parent.getMessageID();

    } // end getMessageID

    public UniStoreMessage getMessage() {
        return m_parent;

    } // end getMessage

    public int getPartIndex() {
        return m_part;

    } // end getPartIndex

    public QualifiedNameKey getPartIdentity() {
        return m_identity;

    } // end getPartIdentity

    public String getMimeType() {
        return m_mimetype;

    } // end getMimeType

    public int getSize() {
        return m_size;

    } // end getSize

    public int getNumReads() {
        return m_nread;

    } // end getNumReads

    public java.util.Date getLastReadDate() {
        return m_lastread;

    } // end getLastReadDate

    public void touchRead() throws DatabaseException {
        if (m_ops == null)
            throw new DatabaseException(BinaryPartImpl.class, "UniStoreMessages", "part.deleted");
        synchronized (this) { // touch the database, then the local values
            java.util.Date tmp = m_ops.touchRead(m_parent.getMessageID(), m_part);
            m_nread++;
            m_lastread = tmp;

        } // end synchronized block

        m_post.postUpdate(new MessagePartRead(this));

    } // end touchRead

    public synchronized void delete(DynamoUser caller) throws DatabaseException, DynamoSecurityException {
        if (m_ops == null)
            throw new DatabaseException(BinaryPartImpl.class, "UniStoreMessages", "part.deleted");
        m_parent.testPermission(caller, Namespaces.UNISTORE_PERMISSIONS_NAMESPACE, "delete.part", "no.deletePart");

        // Cut this object loose from the parent.
        m_parent.deletedBinaryPart(m_part, m_identity);

        // Zap it from the database.
        m_ops.delete(m_parent.getMessageID(), m_part);

        baleeted(); // BALEETED!

    } // end delete

    /*--------------------------------------------------------------------------------
     * Implementations from interface DataItem
     *--------------------------------------------------------------------------------
     */

    public String getName() {
        return m_filename;

    } // end getName

    public InputStream getDataStream() throws IOException {
        if (m_ops == null) { // we've been deleted!
            IOException ioe = new IOException("Part has been deleted");
            ioe.initCause(new DatabaseException(BinaryPartImpl.class, "UniStoreMessages", "part.deleted"));
            throw ioe;

        } // end if

        try { // call through to the database to get the data stream
            return m_ops.getData(m_parent.getMessageID(), m_part);

        } // end try
        catch (DatabaseException e) { // create an IOException and chain this one onto it
            IOException ioe = new IOException("Database error retrieving data stream");
            ioe.initCause(e);
            throw ioe;

        } // end catch

    } // end getDataStream

    public Blob getBlob() throws SQLException {
        if (m_ops == null) { // we've been deleted!
            SQLException se = new SQLException("Part has been deleted");
            se.initCause(new DatabaseException(BinaryPartImpl.class, "UniStoreMessages", "part.deleted"));
            throw se;

        } // end if

        try { // load the data from the database and create the Blob around it
            byte[] data = new byte[m_size];
            m_ops.getData(m_parent.getMessageID(), m_part, data);
            return new ReturnBlob(data);

        } // end try
        catch (DatabaseException e) { // create a SQLException and chain this one onto it
            SQLException se = new SQLException("Database error retrieving data stream");
            se.initCause(e);
            throw se;

        } // end catch
        catch (IOException e) { // create a SQLException and chain this one onto it
            SQLException se = new SQLException("I/O error retrieving data stream");
            se.initCause(e);
            throw se;

        } // end catch

    } // end getBlob

    /*--------------------------------------------------------------------------------
     * Implementations from interface UniStoreBinaryPart
     *--------------------------------------------------------------------------------
     */

    /*--------------------------------------------------------------------------------
     * External operations
     *--------------------------------------------------------------------------------
     */

    void resetPartNumber(int new_num) {
        m_part = new_num;

    } // end resetPartNumber

    /**
     * Called after the part has been deleted, either alone or through the entire message being deleted.  This
     * method nulls out the internal data of the object and posts a "part-deleted" notification.<P>
     * See <A HREF="http://www.homestarrunner.com/sbemail50.html">this page</A> for the source of the method name.
     */
    synchronized void baleeted() {
        // Cut loose most of our data before we post an update event.
        m_ops = null;
        m_nscache = null;
        m_mimetype = null;
        m_size = -1;
        m_filename = null;
        m_nread = 0;
        m_lastread = null;
        m_properties.clear();

        // Post the "deleted" notification event.
        m_post.postUpdate(new MessagePartDeletedEvent(this));

        // Cut loose the rest of our data.
        m_post = null;
        m_parent = null;
        m_part = -1;
        m_identity = null;

    } // end baleeted

} // end class BinaryPartImpl