Java tutorial
/* * 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