969 lines
34 KiB
Java
969 lines
34 KiB
Java
/*
|
|
* 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
|