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.security.acl.AclNotFoundException; import java.util.*; import java.util.regex.*; import org.apache.commons.collections.*; import org.apache.log4j.Logger; import com.silverwrist.dynamo.Namespaces; import com.silverwrist.dynamo.db.NamespaceCache; import com.silverwrist.dynamo.db.UserManagement; import com.silverwrist.dynamo.event.*; import com.silverwrist.dynamo.except.*; import com.silverwrist.dynamo.iface.*; import com.silverwrist.dynamo.security.SecurityReferenceMonitor; import com.silverwrist.dynamo.util.*; class MessageImpl implements UniStoreMessage { /*-------------------------------------------------------------------------------- * Static data members *-------------------------------------------------------------------------------- */ private static Logger logger = Logger.getLogger(MessageImpl.class); private static final Integer NO_READS = new Integer(0); private static Pattern NEWLINES; /*-------------------------------------------------------------------------------- * Attributes *-------------------------------------------------------------------------------- */ private MessageOps m_ops; // database operations object private NamespaceCache m_nscache; // namespace cache private SecurityReferenceMonitor m_srm; // security reference monitor private UserManagement m_users; // user manager private PostDynamicUpdate m_post; // dynamic update poster private long m_id; // the message ID private long m_parentid; // the parent message ID private int m_seq; // sequence within parent private int m_creator; // UID of creator private java.util.Date m_posted; // date message was posted private int m_aclid = -1; // ACL id private ReferenceMap m_properties; // properties cache private int m_text_count = -1; // number of text parts private int m_binary_count = -1; // number of binary parts private ReferenceMap m_part_to_text; // mapping from part index to text part private ReferenceMap m_pk_to_text; // mapping from property key to text part private ReferenceMap m_part_to_binary; // mapping from part index to binary part private ReferenceMap m_pk_to_binary; // mapping from property key to binary part /*-------------------------------------------------------------------------------- * Constructor *-------------------------------------------------------------------------------- */ MessageImpl(MessageOps ops, NamespaceCache nscache, SecurityReferenceMonitor srm, UserManagement users, PostDynamicUpdate post, Map params) { m_ops = ops; m_nscache = nscache; m_srm = srm; m_users = users; m_post = post; m_id = ((Long) (params.get(ManagerOps.PARAM_MSGID))).longValue(); m_parentid = ((Long) (params.get(ManagerOps.PARAM_PARENT))).longValue(); m_seq = ((Integer) (params.get(ManagerOps.PARAM_SEQ))).intValue(); m_creator = ((Integer) (params.get(ManagerOps.PARAM_CREATOR))).intValue(); m_posted = (java.util.Date) (params.get(ManagerOps.PARAM_POSTED)); Integer tmp = (Integer) (params.get(ManagerOps.PARAM_ACLID)); if (tmp != null) m_aclid = tmp.intValue(); m_properties = new ReferenceMap(ReferenceMap.HARD, ReferenceMap.SOFT); m_part_to_text = new ReferenceMap(ReferenceMap.HARD, ReferenceMap.SOFT); m_pk_to_text = new ReferenceMap(ReferenceMap.HARD, ReferenceMap.SOFT); m_part_to_binary = new ReferenceMap(ReferenceMap.HARD, ReferenceMap.SOFT); m_pk_to_binary = new ReferenceMap(ReferenceMap.HARD, ReferenceMap.SOFT); } // end constructor /*-------------------------------------------------------------------------------- * Internal operations *-------------------------------------------------------------------------------- */ private static final int getLineCount(String s) { Matcher m = NEWLINES.matcher(s); int rc = 1; while (m.find()) rc++; return rc; } // end getLineCount private final UniStoreTextPart createTextPart(int nsid, String name, String mimetype, int charcount, int linecount, String text) throws DatabaseException { // Call down to the database to create the part. int partnum = m_ops.createTextPart(m_id, nsid, name, mimetype, charcount, linecount, text); // Fake up a parameter buffer to create the part object. Integer key1 = new Integer(partnum); PropertyKey key2 = new PropertyKey(nsid, name); HashMap params = new HashMap(); params.put(MessageOps.PARAM_PART, key1); params.put(MessageOps.PARAM_IDENTITY, key2); if (mimetype != null) params.put(MessageOps.PARAM_MIMETYPE, mimetype); params.put(MessageOps.PARAM_SIZE, new Integer(charcount)); params.put(MessageOps.PARAM_LINECOUNT, new Integer(linecount)); params.put(MessageOps.PARAM_READS, NO_READS); TextPartImpl rc = new TextPartImpl(m_ops.getTextPartOps(), m_nscache, m_post, this, params); rc.precacheText(text); synchronized (this) { // Add the text part to the internal caches. m_part_to_text.put(key1, rc); m_pk_to_text.put(key2, rc); if (m_text_count >= 0) m_text_count++; } // end synchronized block m_post.postUpdate(new MessagePartAddedEvent(rc)); return rc; } // end createTextPart private final UniStoreBinaryPart createBinaryPart(int nsid, String name, String mimetype, String filename, int length, InputStream data) throws DatabaseException { // Call down to the database to create the part. int partnum = m_ops.createBinaryPart(m_id, nsid, name, mimetype, filename, length, data); // Fake up a parameter buffer to create the part object. Integer key1 = new Integer(partnum); PropertyKey key2 = new PropertyKey(nsid, name); HashMap params = new HashMap(); params.put(MessageOps.PARAM_PART, key1); params.put(MessageOps.PARAM_IDENTITY, key2); if (mimetype != null) params.put(MessageOps.PARAM_MIMETYPE, mimetype); params.put(MessageOps.PARAM_SIZE, new Integer(length)); if (filename != null) params.put(MessageOps.PARAM_FILENAME, filename); params.put(MessageOps.PARAM_READS, NO_READS); BinaryPartImpl rc = new BinaryPartImpl(m_ops.getBinaryPartOps(), m_nscache, m_post, this, params); synchronized (this) { // Add the binary part to the internal caches. m_part_to_binary.put(key1, rc); m_pk_to_binary.put(key2, rc); if (m_binary_count >= 0) m_binary_count++; } // end synchronized block m_post.postUpdate(new MessagePartAddedEvent(rc)); return rc; } // end createBinaryPart /*-------------------------------------------------------------------------------- * Overrides from class Object *-------------------------------------------------------------------------------- */ public String toString() { if (m_ops == null) return "(deleted message)"; return "message " + m_id; } // end toString /*-------------------------------------------------------------------------------- * Implementations from interface ObjectProvider *-------------------------------------------------------------------------------- */ /** * Retrieves an object from this message's properties. * * @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_id, 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 message's properties. * * @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 message's properties. * @return The previous object that was set into the message's properties 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 message's properties. */ public Object setObject(DynamoUser caller, String namespace, String name, Object value) throws DatabaseException, DynamoSecurityException { if (m_ops == null) throw new DatabaseException(MessageImpl.class, "UniStoreMessages", "message.deleted"); 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_id, key, value); // and cache it, too m_properties.put(key, value); } // end synchronized block m_post.postUpdate(new MessagePropertyUpdateEvent(this, namespace, name)); return rc; } // end setObject /** * Removes an object from this message's properties. * * @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 message's properties 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 message's properties. */ public Object removeObject(DynamoUser caller, String namespace, String name) throws DatabaseException, DynamoSecurityException { if (m_ops == null) throw new DatabaseException(MessageImpl.class, "UniStoreMessages", "message.deleted"); 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_id, key); // and remove the cached value, too m_properties.remove(key); } // end synchronized block m_post.postUpdate(new MessagePropertyUpdateEvent(this, namespace, name)); return rc; } // end removeObject /** * Returns a collection of all object namespaces that have been set into this message's properties. * * @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_id); 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 message's properties 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_id, 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 UniStoreMessage *-------------------------------------------------------------------------------- */ public long getMessageID() { return m_id; } // end getMessageID public long getParentMessageID() { return m_parentid; } // end getParentMessageID public void setParentMessageID(DynamoUser caller, long id) throws DatabaseException, DynamoSecurityException { testPermission(caller, Namespaces.UNISTORE_PERMISSIONS_NAMESPACE, "set.parent", "no.setParent"); synchronized (this) { // reset the parent message ID m_ops.setParentMessageID(m_id, id); m_parentid = id; } // end synchronized block m_post.postUpdate(new MessagePositionUpdateEvent(this, m_parentid, m_seq)); } // end setParentMessageID public int getSequence() { return m_seq; } // end getSequence public void setSequence(DynamoUser caller, int seq) throws DatabaseException, DynamoSecurityException { testPermission(caller, Namespaces.UNISTORE_PERMISSIONS_NAMESPACE, "set.parent", "no.setParent"); synchronized (this) { // reset the sequence number m_ops.setSequence(m_id, seq); m_seq = seq; } // end if m_post.postUpdate(new MessagePositionUpdateEvent(this, m_parentid, m_seq)); } // end setSequence public int getCreatorUID() { return m_creator; } // end getCreatorUID public DynamoUser getCreator() throws DatabaseException { if (m_ops == null) throw new DatabaseException(MessageImpl.class, "UniStoreMessages", "message.deleted"); return (m_users == null) ? null : m_users.getUser(m_creator); } // end getCreator public java.util.Date getPostDate() { return m_posted; } // end getPostDate public DynamoAcl getAcl() throws DatabaseException, AclNotFoundException { if (m_ops == null) throw new DatabaseException(MessageImpl.class, "UniStoreMessages", "message.deleted"); if ((m_aclid < 0) || (m_srm == null)) return null; else return m_srm.getAcl(m_aclid); } // end getAcl public void setAcl(DynamoUser caller, DynamoAcl acl) throws DatabaseException, DynamoSecurityException { testPermission(caller, Namespaces.UNISTORE_PERMISSIONS_NAMESPACE, "set.ACL", "no.setACL"); int aclid = -1; if (acl != null) aclid = acl.getAclID(); synchronized (this) { // reset the ACL ID m_ops.setAclID(m_id, aclid); m_aclid = aclid; } // end synchronized block m_post.postUpdate(new MessageSecurityUpdateEvent(this, acl)); } // end setAcl public synchronized int getNumTextParts() throws DatabaseException { if (m_ops == null) throw new DatabaseException(MessageImpl.class, "UniStoreMessages", "message.deleted"); if (m_text_count < 0) m_text_count = m_ops.getNumTextParts(m_id); return m_text_count; } // end getNumTextParts public synchronized int getNumBinaryParts() throws DatabaseException { if (m_ops == null) throw new DatabaseException(MessageImpl.class, "UniStoreMessages", "message.deleted"); if (m_binary_count < 0) m_binary_count = m_ops.getNumBinaryParts(m_id); return m_binary_count; } // end getNumBinaryParts public UniStoreTextPart getTextPart(int index) throws DatabaseException { if (m_ops == null) throw new DatabaseException(MessageImpl.class, "UniStoreMessages", "message.deleted"); Integer key = new Integer(index); TextPartImpl rc = null; synchronized (this) { // look in the part-to-text map first rc = (TextPartImpl) (m_part_to_text.get(key)); if (rc == null) { // OK, look up the part in the database Map params = m_ops.loadTextPart(m_id, index); PropertyKey otherkey = (PropertyKey) (params.get(MessageOps.PARAM_IDENTITY)); rc = (TextPartImpl) (m_pk_to_text.get(otherkey)); if (rc == null) { // need to create a new object rc = new TextPartImpl(m_ops.getTextPartOps(), m_nscache, m_post, this, params); m_part_to_text.put(key, rc); m_pk_to_text.put(otherkey, rc); } // end if else // feed back into the parts map m_part_to_text.put(key, rc); } // end if else { // need to reinsert into property key map QualifiedNameKey qnk = rc.getPartIdentity(); PropertyKey pk = new PropertyKey(m_nscache.namespaceNameToId(qnk.getNamespace()), qnk.getName()); m_pk_to_text.put(pk, rc); } // end else } // end synchronized block return rc; } // end getTextPart public UniStoreTextPart getTextPart(String namespace, String name) throws DatabaseException { if (m_ops == null) throw new DatabaseException(MessageImpl.class, "UniStoreMessages", "message.deleted"); PropertyKey key = new PropertyKey(m_nscache.namespaceNameToId(namespace), name); TextPartImpl rc = null; synchronized (this) { // look in the PK-to-text map first rc = (TextPartImpl) (m_pk_to_text.get(key)); if (rc == null) { // OK, look up the part in the database Map params = m_ops.loadTextPart(m_id, key); Integer otherkey = (Integer) (params.get(MessageOps.PARAM_PART)); rc = (TextPartImpl) (m_part_to_text.get(otherkey)); if (rc == null) { // OK, need to create the object rc = new TextPartImpl(m_ops.getTextPartOps(), m_nscache, m_post, this, params); m_part_to_text.put(otherkey, rc); m_pk_to_text.put(key, rc); } // end if else // feed back into the PKs map m_pk_to_text.put(key, rc); } // end if else // reinsert into part key map m_part_to_text.put(new Integer(rc.getPartIndex()), rc); } // end synchronized block return rc; } // end getTextPart public UniStoreBinaryPart getBinaryPart(int index) throws DatabaseException { if (m_ops == null) throw new DatabaseException(MessageImpl.class, "UniStoreMessages", "message.deleted"); Integer key = new Integer(index); BinaryPartImpl rc = null; synchronized (this) { // look in the part-to-binary map first rc = (BinaryPartImpl) (m_part_to_binary.get(key)); if (rc == null) { // OK, look up the part in the database Map params = m_ops.loadBinaryPart(m_id, index); PropertyKey otherkey = (PropertyKey) (params.get(MessageOps.PARAM_IDENTITY)); rc = (BinaryPartImpl) (m_pk_to_binary.get(otherkey)); if (rc == null) { // need to create a new object rc = new BinaryPartImpl(m_ops.getBinaryPartOps(), m_nscache, m_post, this, params); m_part_to_binary.put(key, rc); m_pk_to_binary.put(otherkey, rc); } // end if else // feed back into the parts map m_part_to_binary.put(key, rc); } // end if else { // need to reinsert into property key map QualifiedNameKey qnk = rc.getPartIdentity(); PropertyKey pk = new PropertyKey(m_nscache.namespaceNameToId(qnk.getNamespace()), qnk.getName()); m_pk_to_binary.put(pk, rc); } // end else } // end synchronized block return rc; } // end getBinaryPart public UniStoreBinaryPart getBinaryPart(String namespace, String name) throws DatabaseException { if (m_ops == null) throw new DatabaseException(MessageImpl.class, "UniStoreMessages", "message.deleted"); PropertyKey key = new PropertyKey(m_nscache.namespaceNameToId(namespace), name); BinaryPartImpl rc = null; synchronized (this) { // look in the PK-to-binary map first rc = (BinaryPartImpl) (m_pk_to_binary.get(key)); if (rc == null) { // OK, look up the part in the database Map params = m_ops.loadBinaryPart(m_id, key); Integer otherkey = (Integer) (params.get(MessageOps.PARAM_PART)); rc = (BinaryPartImpl) (m_part_to_binary.get(otherkey)); if (rc == null) { // OK, need to create the object rc = new BinaryPartImpl(m_ops.getBinaryPartOps(), m_nscache, m_post, this, params); m_part_to_binary.put(otherkey, rc); m_pk_to_binary.put(key, rc); } // end if else // feed back into the PKs map m_pk_to_binary.put(key, rc); } // end if else // reinsert into part key map m_part_to_binary.put(new Integer(rc.getPartIndex()), rc); } // end synchronized block return rc; } // end getBinaryPart public List getTextParts() throws DatabaseException { if (m_ops == null) throw new DatabaseException(MessageImpl.class, "UniStoreMessages", "message.deleted"); int n = this.getNumTextParts(); ArrayList rc = new ArrayList(n); for (int i = 1; i <= n; i++) rc.add(this.getTextPart(i)); return Collections.unmodifiableList(rc); } // end getTextParts public List getBinaryParts() throws DatabaseException { if (m_ops == null) throw new DatabaseException(MessageImpl.class, "UniStoreMessages", "message.deleted"); int n = this.getNumBinaryParts(); ArrayList rc = new ArrayList(n); for (int i = 1; i <= n; i++) rc.add(this.getBinaryPart(i)); return Collections.unmodifiableList(rc); } // end getBinaryParts public UniStoreTextPart createTextPart(DynamoUser caller, String namespace, String name, String mimetype, HTMLChecker data) throws DatabaseException, DynamoSecurityException { testPermission(caller, Namespaces.UNISTORE_PERMISSIONS_NAMESPACE, "create.textPart", "no.createPart"); return createTextPart(m_nscache.namespaceNameToId(namespace), name, mimetype, data.getLength(), data.getLines(), data.getValue()); } // end createTextPart public UniStoreTextPart createTextPart(DynamoUser caller, String namespace, String name, String mimetype, String data) throws DatabaseException, DynamoSecurityException { testPermission(caller, Namespaces.UNISTORE_PERMISSIONS_NAMESPACE, "create.textPart", "no.createPart"); return createTextPart(m_nscache.namespaceNameToId(namespace), name, mimetype, data.length(), getLineCount(data), data); } // end createTextPart public UniStoreBinaryPart createBinaryPart(DynamoUser caller, String namespace, String name, DataItem data) throws IOException, DatabaseException, DynamoSecurityException { testPermission(caller, Namespaces.UNISTORE_PERMISSIONS_NAMESPACE, "create.binaryPart", "no.createPart"); return createBinaryPart(m_nscache.namespaceNameToId(namespace), name, data.getMimeType(), data.getName(), data.getSize(), data.getDataStream()); } // end createBinaryPart public UniStoreBinaryPart createBinaryPart(DynamoUser caller, String namespace, String name, String mimetype, String filename, int length, InputStream data) throws DatabaseException, DynamoSecurityException { testPermission(caller, Namespaces.UNISTORE_PERMISSIONS_NAMESPACE, "create.binaryPart", "no.createPart"); return createBinaryPart(m_nscache.namespaceNameToId(namespace), name, mimetype, filename, length, data); } // end createBinaryPart public synchronized void delete(DynamoUser caller) throws DatabaseException, DynamoSecurityException { testPermission(caller, Namespaces.UNISTORE_PERMISSIONS_NAMESPACE, "delete.message", "no.deleteMessage"); // We need to have lists of the text and binary parts on hand before we delete everything, so that we can // send out notifications. However, not every one of the parts will be in our cache, so we'll need to create // some temporary instances, but use the cached ones whereever feasible. Map pmap = m_ops.listTextParts(m_id); ArrayList text_parts = new ArrayList(pmap.size()); Iterator it = pmap.entrySet().iterator(); while (it.hasNext()) { // look for a matching TextPartImpl Map.Entry ntry = (Map.Entry) (it.next()); TextPartImpl p = (TextPartImpl) (m_part_to_text.get(ntry.getKey())); if (p == null) p = (TextPartImpl) (m_pk_to_text.get(ntry.getValue())); if (p == null) { // create a "scratch" instance PropertyKey pk = (PropertyKey) (ntry.getValue()); QualifiedNameKey qname = new QualifiedNameKey(m_nscache.namespaceIdToName(pk.getNamespaceID()), pk.getName()); p = new TextPartImpl(m_post, this, ((Integer) (ntry.getKey())).intValue(), qname); } // end if text_parts.add(p); } // end while pmap = m_ops.listBinaryParts(m_id); ArrayList binary_parts = new ArrayList(pmap.size()); it = pmap.entrySet().iterator(); while (it.hasNext()) { // look for a matching BinaryPartImpl Map.Entry ntry = (Map.Entry) (it.next()); BinaryPartImpl p = (BinaryPartImpl) (m_part_to_binary.get(ntry.getKey())); if (p == null) p = (BinaryPartImpl) (m_pk_to_binary.get(ntry.getValue())); if (p == null) { // create a "scratch" instance PropertyKey pk = (PropertyKey) (ntry.getValue()); QualifiedNameKey qname = new QualifiedNameKey(m_nscache.namespaceIdToName(pk.getNamespaceID()), pk.getName()); p = new BinaryPartImpl(m_post, this, ((Integer) (ntry.getKey())).intValue(), qname); } // end if binary_parts.add(p); } // end while // Delete the message from the database. m_ops.delete(m_id); // Cut loose most of our data before we start notifying. m_ops = null; m_nscache = null; m_srm = null; m_users = null; m_parentid = -1; m_seq = -1; m_creator = -1; m_posted = null; m_aclid = -1; m_properties.clear(); m_text_count = -1; m_binary_count = -1; m_part_to_text.clear(); m_pk_to_text.clear(); m_part_to_binary.clear(); m_pk_to_binary.clear(); // Send out the deletion notifications (and clear the data) for all parts. it = text_parts.iterator(); while (it.hasNext()) { // make sure all of these parts are BALEETED! TextPartImpl p = (TextPartImpl) (it.next()); p.baleeted(); } // end while text_parts.clear(); it = binary_parts.iterator(); while (it.hasNext()) { // make sure all of these parts are BALEETED! BinaryPartImpl p = (BinaryPartImpl) (it.next()); p.baleeted(); } // end while binary_parts.clear(); // Send our own "BALEETED!" notification. m_post.postUpdate(new MessageDeletedEvent(this)); // Now cut loose the rest of our data. m_post = null; m_id = -1; } // end delete /*-------------------------------------------------------------------------------- * External operations *-------------------------------------------------------------------------------- */ void testPermission(DynamoUser caller, String perm_namespace, String perm_name, String fail_message) throws DatabaseException, DynamoSecurityException { if (m_ops == null) throw new DatabaseException(MessageImpl.class, "UniStoreMessages", "message.deleted"); if (caller.equals(m_srm.getAdminUser())) return; // Administrator can do anything if (m_aclid == -1) { // no ACL - rely on fallback method if (caller.getUID() == m_creator) return; // the creator can do anything, but no one else can } // end if else { // test against the ACL try { // look up the ACL if (m_srm.getAcl(m_aclid).testPermission(caller, perm_namespace, perm_name)) return; } // end try catch (AclNotFoundException e) { // ACL not found - go to fallback mechanism logger.warn("ACL " + m_aclid + " not found while testing message " + m_id); if (caller.getUID() == m_creator) return; // the creator can do anything, but no one else can } // end catch } // end else // Throw a DynamoSecurityException indicating what you're not permitted to do. DynamoSecurityException de = new DynamoSecurityException(MessageImpl.class, "UniStoreMessages", fail_message); de.setParameter(0, String.valueOf(m_id)); throw de; } // end testPermission void zeroCounts() { m_text_count = 0; m_binary_count = 0; } // end zeroCounts void deletedTextPart(int partnum, QualifiedNameKey identity) throws DatabaseException { PropertyKey pk = new PropertyKey(m_nscache.namespaceNameToId(identity.getNamespace()), identity.getName()); synchronized (this) { // Remove the entry from the cache reference maps. m_part_to_text.remove(new Integer(partnum)); m_pk_to_text.remove(pk); if (m_text_count >= 0) m_text_count--; // All entries with a part number higher than the deleted part number have to be renumbered downwards. // First, scan through the keyset to find all the appropriate part numbers, get their values, and lock them // into a hard HashMap to keep them in memory while we do this. HashMap temp = new HashMap(); Iterator it = m_part_to_text.keySet().iterator(); while (it.hasNext()) { // get each key in turn and check it Integer key = (Integer) (it.next()); if (key.intValue() > partnum) { // now see if the object's in memory TextPartImpl obj = (TextPartImpl) (m_part_to_text.get(key)); if (obj != null) temp.put(key, obj); } // end if } // end while // Now go through, poke new part numbers into each of these parts, and get them into the parts // mapping correctly. it = temp.entrySet().iterator(); while (it.hasNext()) { // get each part in turn and deal with it Map.Entry ntry = (Map.Entry) (it.next()); m_part_to_text.remove(ntry.getKey()); int new_partnum = ((Integer) (ntry.getKey())).intValue() - 1; TextPartImpl obj = (TextPartImpl) (ntry.getValue()); obj.resetPartNumber(new_partnum); m_part_to_text.put(new Integer(new_partnum), obj); } // end while temp.clear(); // release the extra references } // end synchronized block } // end deletedTextPart void deletedBinaryPart(int partnum, QualifiedNameKey identity) throws DatabaseException { PropertyKey pk = new PropertyKey(m_nscache.namespaceNameToId(identity.getNamespace()), identity.getName()); synchronized (this) { // Remove the entry from the cache reference maps. m_part_to_binary.remove(new Integer(partnum)); m_pk_to_binary.remove(pk); if (m_binary_count >= 0) m_binary_count--; // All entries with a part number higher than the deleted part number have to be renumbered downwards. // First, scan through the keyset to find all the appropriate part numbers, get their values, and lock them // into a hard HashMap to keep them in memory while we do this. HashMap temp = new HashMap(); Iterator it = m_part_to_binary.keySet().iterator(); while (it.hasNext()) { // get each key in turn and check it Integer key = (Integer) (it.next()); if (key.intValue() > partnum) { // now see if the object's in memory BinaryPartImpl obj = (BinaryPartImpl) (m_part_to_binary.get(key)); if (obj != null) temp.put(key, obj); } // end if } // end while // Now go through, poke new part numbers into each of these parts, and get them into the parts // mapping correctly. it = temp.entrySet().iterator(); while (it.hasNext()) { // get each part in turn and deal with it Map.Entry ntry = (Map.Entry) (it.next()); m_part_to_binary.remove(ntry.getKey()); int new_partnum = ((Integer) (ntry.getKey())).intValue() - 1; BinaryPartImpl obj = (BinaryPartImpl) (ntry.getValue()); obj.resetPartNumber(new_partnum); m_part_to_binary.put(new Integer(new_partnum), obj); } // end while temp.clear(); // release the extra references } // end synchronized block } // end deletedBinaryPart /*-------------------------------------------------------------------------------- * Static initializer *-------------------------------------------------------------------------------- */ static { try { // set up our patterns NEWLINES = Pattern.compile("\\r?\\n?"); // matches CR, LF, or CRLF } // end try catch (PatternSyntaxException e) { // just log the error logger.fatal("Pattern compile error in MessageImpl", e); } // end catch } // end static initializer } // end class MessageImpl