+ * See this page 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
diff --git a/src/dynamo-framework/com/silverwrist/dynamo/unistore/BinaryPartOps.java b/src/dynamo-framework/com/silverwrist/dynamo/unistore/BinaryPartOps.java
index 50e63a5..b7b4e8b 100644
--- a/src/dynamo-framework/com/silverwrist/dynamo/unistore/BinaryPartOps.java
+++ b/src/dynamo-framework/com/silverwrist/dynamo/unistore/BinaryPartOps.java
@@ -59,4 +59,6 @@ abstract class BinaryPartOps extends OpsBase
abstract int getData(long msgid, int part, byte[] here) throws IOException, DatabaseException;
+ abstract void delete(long msgid, int part) throws DatabaseException;
+
} // end class BinaryPartOps
diff --git a/src/dynamo-framework/com/silverwrist/dynamo/unistore/BinaryPartOps_mysql.java b/src/dynamo-framework/com/silverwrist/dynamo/unistore/BinaryPartOps_mysql.java
index c55e1c2..9e800af 100644
--- a/src/dynamo-framework/com/silverwrist/dynamo/unistore/BinaryPartOps_mysql.java
+++ b/src/dynamo-framework/com/silverwrist/dynamo/unistore/BinaryPartOps_mysql.java
@@ -503,4 +503,57 @@ class BinaryPartOps_mysql extends BinaryPartOps
} // end getData
+ void delete(long msgid, int part) throws DatabaseException
+ {
+ Connection conn = null;
+ PreparedStatement stmt = null;
+ Statement stmt2 = null;
+ try
+ { // get a connection
+ conn = getConnection();
+
+ // lock the tables
+ stmt2 = conn.createStatement();
+ stmt2.executeUpdate("LOCK TABLES us_binary WRITE, us_binary_props WRITE;");
+
+ // delete all entries from the main and property tables
+ stmt = conn.prepareStatement("DELETE FROM us_binary WHERE msgid = ? AND part = ?;");
+ stmt.setLong(1,msgid);
+ stmt.setInt(2,part);
+ stmt.executeUpdate();
+ stmt.close();
+ stmt = conn.prepareStatement("DELETE FROM us_binary_prop WHERE msgid = ? AND part = ?;");
+ stmt.setLong(1,msgid);
+ stmt.setInt(2,part);
+ stmt.executeUpdate();
+ stmt.close();
+
+ // renumber all entries above that one to close the gap
+ stmt = conn.prepareStatement("UPDATE us_binary SET part = part - 1 WHERE msgid = ? AND part > ?;");
+ stmt.setLong(1,msgid);
+ stmt.setInt(2,part);
+ stmt.executeUpdate();
+ stmt.close();
+ stmt = conn.prepareStatement("UPDATE us_binary_prop SET part = part - 1 WHERE msgid = ? AND part > ?;");
+ stmt.setLong(1,msgid);
+ stmt.setInt(2,part);
+ stmt.executeUpdate();
+
+ } // end try
+ catch (SQLException e)
+ { // translate to a general DatabaseException
+ throw generalException(e);
+
+ } // end catch
+ finally
+ { // shut everything down
+ MySQLUtils.unlockTables(conn);
+ SQLUtils.shutdown(stmt);
+ SQLUtils.shutdown(stmt2);
+ SQLUtils.shutdown(conn);
+
+ } // end finally
+
+ } // end delete
+
} // end class BinaryPartOps_mysql
diff --git a/src/dynamo-framework/com/silverwrist/dynamo/unistore/ManagerOps.java b/src/dynamo-framework/com/silverwrist/dynamo/unistore/ManagerOps.java
index b30f40a..ae1b09c 100644
--- a/src/dynamo-framework/com/silverwrist/dynamo/unistore/ManagerOps.java
+++ b/src/dynamo-framework/com/silverwrist/dynamo/unistore/ManagerOps.java
@@ -77,6 +77,8 @@ abstract class ManagerOps extends OpsBase
abstract Map getMessageData(long msgid) throws DatabaseException;
+ abstract long createMessage(int creator, java.util.Date postdate) throws DatabaseException;
+
/*--------------------------------------------------------------------------------
* External operations
*--------------------------------------------------------------------------------
diff --git a/src/dynamo-framework/com/silverwrist/dynamo/unistore/ManagerOps_mysql.java b/src/dynamo-framework/com/silverwrist/dynamo/unistore/ManagerOps_mysql.java
index 925fd33..ccf0674 100644
--- a/src/dynamo-framework/com/silverwrist/dynamo/unistore/ManagerOps_mysql.java
+++ b/src/dynamo-framework/com/silverwrist/dynamo/unistore/ManagerOps_mysql.java
@@ -116,4 +116,43 @@ public class ManagerOps_mysql extends ManagerOps
} // end getMessageData
+ long createMessage(int creator, java.util.Date postdate) throws DatabaseException
+ {
+ Connection conn = null;
+ PreparedStatement stmt = null;
+ Statement stmt2 = null;
+ try
+ { // get a connection
+ conn = getConnection();
+
+ // lock the base table
+ stmt2 = conn.createStatement();
+ stmt2.executeUpdate("LOCK TABLES us_head WRITE;");
+
+ // create and execute the INSERT statement
+ stmt = conn.prepareStatement("INSERT INTO us_head (creator, posted) VALUES (?, ?);");
+ stmt.setInt(1,creator);
+ m_utils.setDateTime(stmt,2,postdate);
+ stmt.executeUpdate();
+
+ // return the new message ID
+ return MySQLUtils.getLastInsertLong(conn);
+
+ } // end try
+ catch (SQLException e)
+ { // translate to a general DatabaseException
+ throw generalException(e);
+
+ } // end catch
+ finally
+ { // shut everything down
+ MySQLUtils.unlockTables(conn);
+ SQLUtils.shutdown(stmt);
+ SQLUtils.shutdown(stmt2);
+ SQLUtils.shutdown(conn);
+
+ } // end finally
+
+ } // end createMessage
+
} // end class ManagerOps_mysql
diff --git a/src/dynamo-framework/com/silverwrist/dynamo/unistore/MessageImpl.java b/src/dynamo-framework/com/silverwrist/dynamo/unistore/MessageImpl.java
index e17cef6..bd4f818 100644
--- a/src/dynamo-framework/com/silverwrist/dynamo/unistore/MessageImpl.java
+++ b/src/dynamo-framework/com/silverwrist/dynamo/unistore/MessageImpl.java
@@ -17,8 +17,10 @@
*/
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;
@@ -39,6 +41,10 @@ class MessageImpl implements UniStoreMessage
private static Logger logger = Logger.getLogger(MessageImpl.class);
+ private static final Integer NO_READS = new Integer(0);
+
+ private static Pattern NEWLINES;
+
/*--------------------------------------------------------------------------------
* Attributes
*--------------------------------------------------------------------------------
@@ -92,13 +98,109 @@ class MessageImpl implements UniStoreMessage
} // 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 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.
@@ -106,6 +208,9 @@ class MessageImpl implements UniStoreMessage
*/
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);
@@ -157,6 +262,8 @@ class MessageImpl implements UniStoreMessage
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
@@ -190,6 +297,8 @@ class MessageImpl implements UniStoreMessage
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
@@ -217,6 +326,9 @@ class MessageImpl implements UniStoreMessage
*/
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);
@@ -238,6 +350,9 @@ class MessageImpl implements UniStoreMessage
*/
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);
@@ -320,7 +435,9 @@ class MessageImpl implements UniStoreMessage
public DynamoUser getCreator() throws DatabaseException
{
- return m_users.getUser(m_creator);
+ if (m_ops==null)
+ throw new DatabaseException(MessageImpl.class,"UniStoreMessages","message.deleted");
+ return (m_users==null) ? null : m_users.getUser(m_creator);
} // end getCreator
@@ -332,7 +449,12 @@ class MessageImpl implements UniStoreMessage
public DynamoAcl getAcl() throws DatabaseException, AclNotFoundException
{
- return m_srm.getAcl(m_aclid);
+ 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
@@ -355,6 +477,8 @@ class MessageImpl implements UniStoreMessage
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;
@@ -363,6 +487,8 @@ class MessageImpl implements UniStoreMessage
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;
@@ -371,6 +497,8 @@ class MessageImpl implements UniStoreMessage
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)
@@ -408,6 +536,8 @@ class MessageImpl implements UniStoreMessage
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)
@@ -440,6 +570,8 @@ class MessageImpl implements UniStoreMessage
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)
@@ -477,6 +609,8 @@ class MessageImpl implements UniStoreMessage
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)
@@ -509,6 +643,8 @@ class MessageImpl implements UniStoreMessage
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++)
@@ -519,6 +655,8 @@ class MessageImpl implements UniStoreMessage
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++)
@@ -527,6 +665,140 @@ class MessageImpl implements UniStoreMessage
} // 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
*--------------------------------------------------------------------------------
@@ -535,6 +807,8 @@ class MessageImpl implements UniStoreMessage
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)
@@ -568,4 +842,127 @@ class MessageImpl implements UniStoreMessage
} // 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
diff --git a/src/dynamo-framework/com/silverwrist/dynamo/unistore/MessageOps.java b/src/dynamo-framework/com/silverwrist/dynamo/unistore/MessageOps.java
index ebce3aa..0dc6a40 100644
--- a/src/dynamo-framework/com/silverwrist/dynamo/unistore/MessageOps.java
+++ b/src/dynamo-framework/com/silverwrist/dynamo/unistore/MessageOps.java
@@ -17,6 +17,7 @@
*/
package com.silverwrist.dynamo.unistore;
+import java.io.*;
import java.util.*;
import com.silverwrist.dynamo.db.OpsBase;
import com.silverwrist.dynamo.except.*;
@@ -112,6 +113,18 @@ abstract class MessageOps extends OpsBase
abstract Map loadBinaryPart(long msgid, PropertyKey identity) throws DatabaseException;
+ abstract int createTextPart(long msgid, int nsid, String name, String mimetype, int charcount, int linecount,
+ String data) throws DatabaseException;
+
+ abstract int createBinaryPart(long msgid, int nsid, String name, String mimetype, String filename, int length,
+ InputStream data) throws DatabaseException;
+
+ abstract void delete(long msgid) throws DatabaseException;
+
+ abstract Map listTextParts(long msgid) throws DatabaseException;
+
+ abstract Map listBinaryParts(long msgid) throws DatabaseException;
+
/*--------------------------------------------------------------------------------
* External operations
*--------------------------------------------------------------------------------
diff --git a/src/dynamo-framework/com/silverwrist/dynamo/unistore/MessageOps_mysql.java b/src/dynamo-framework/com/silverwrist/dynamo/unistore/MessageOps_mysql.java
index 1911c20..a9723b2 100644
--- a/src/dynamo-framework/com/silverwrist/dynamo/unistore/MessageOps_mysql.java
+++ b/src/dynamo-framework/com/silverwrist/dynamo/unistore/MessageOps_mysql.java
@@ -17,6 +17,7 @@
*/
package com.silverwrist.dynamo.unistore;
+import java.io.*;
import java.sql.*;
import java.util.*;
import com.silverwrist.util.*;
@@ -790,4 +791,286 @@ class MessageOps_mysql extends MessageOps
} // end loadBinaryPart
+ int createTextPart(long msgid, int nsid, String name, String mimetype, int charcount, int linecount, String data)
+ throws DatabaseException
+ {
+ Connection conn = null;
+ PreparedStatement stmt = null;
+ Statement stmt2 = null;
+ ResultSet rs = null;
+ try
+ { // get a connection
+ conn = getConnection();
+
+ // lock the table
+ stmt2 = conn.createStatement();
+ stmt2.executeUpdate("LOCK TABLES us_text WRITE;");
+
+ // start by seeing if the identity already exists
+ stmt = conn.prepareStatement("SELECT part FROM us_text WHERE msgid = ? AND ident_nsid = ? AND ident_name = ?;");
+ stmt.setLong(1,msgid);
+ stmt.setInt(2,nsid);
+ stmt.setString(3,name);
+ rs = stmt.executeQuery();
+ if (rs.next())
+ { // identity already exists, throw exception
+ DatabaseException de = new DatabaseException(MessageOps_mysql.class,"UniStoreMessages","part.exists");
+ de.setParameter(0,String.valueOf(msgid));
+ throw de;
+
+ } // end if
+
+ SQLUtils.shutdown(rs);
+ rs = null;
+ SQLUtils.shutdown(stmt);
+
+ // get the part number for this part
+ stmt = conn.prepareStatement("SELECT IFNULL(MAX(part),0) FROM us_text WHERE msgid = ?;");
+ stmt.setLong(1,msgid);
+ rs = stmt.executeQuery();
+ int partnum = SQLUtils.getReturnCountInt(rs,1) + 1;
+
+ SQLUtils.shutdown(rs);
+ rs = null;
+ SQLUtils.shutdown(stmt);
+
+ // perform the insert!
+ stmt = conn.prepareStatement("INSERT INTO us_text (msgid, part, ident_nsid, ident_name, mimetype, charcount, "
+ + "linecount, data) VALUES (?, ?, ?, ?, ?, ?, ?, ?);");
+ stmt.setLong(1,msgid);
+ stmt.setInt(2,partnum);
+ stmt.setInt(3,nsid);
+ stmt.setString(4,name);
+ stmt.setString(5,mimetype);
+ stmt.setInt(6,charcount);
+ stmt.setInt(7,linecount);
+ stmt.setString(8,data);
+ stmt.executeUpdate();
+
+ return partnum;
+
+ } // end try
+ catch (SQLException e)
+ { // translate to a general DatabaseException
+ throw generalException(e);
+
+ } // end catch
+ finally
+ { // shut everything down
+ MySQLUtils.unlockTables(conn);
+ SQLUtils.shutdown(rs);
+ SQLUtils.shutdown(stmt);
+ SQLUtils.shutdown(stmt2);
+ SQLUtils.shutdown(conn);
+
+ } // end finally
+
+ } // end createTextPart
+
+ int createBinaryPart(long msgid, int nsid, String name, String mimetype, String filename, int length,
+ InputStream data) throws DatabaseException
+ {
+ Connection conn = null;
+ PreparedStatement stmt = null;
+ Statement stmt2 = null;
+ ResultSet rs = null;
+ try
+ { // get a connection
+ conn = getConnection();
+
+ // lock the table
+ stmt2 = conn.createStatement();
+ stmt2.executeUpdate("LOCK TABLES us_binary WRITE;");
+
+ // start by seeing if the identity already exists
+ stmt = conn.prepareStatement("SELECT part FROM us_binary WHERE msgid = ? AND ident_nsid = ? "
+ + "AND ident_name = ?;");
+ stmt.setLong(1,msgid);
+ stmt.setInt(2,nsid);
+ stmt.setString(3,name);
+ rs = stmt.executeQuery();
+ if (rs.next())
+ { // identity already exists, throw exception
+ DatabaseException de = new DatabaseException(MessageOps_mysql.class,"UniStoreMessages","part.exists");
+ de.setParameter(0,String.valueOf(msgid));
+ throw de;
+
+ } // end if
+
+ SQLUtils.shutdown(rs);
+ rs = null;
+ SQLUtils.shutdown(stmt);
+
+ // get the part number for this part
+ stmt = conn.prepareStatement("SELECT IFNULL(MAX(part),0) FROM us_binary WHERE msgid = ?;");
+ stmt.setLong(1,msgid);
+ rs = stmt.executeQuery();
+ int partnum = SQLUtils.getReturnCountInt(rs,1) + 1;
+
+ SQLUtils.shutdown(rs);
+ rs = null;
+ SQLUtils.shutdown(stmt);
+
+ // perform the insert!
+ stmt = conn.prepareStatement("INSERT INTO us_binary (msgid, part, ident_nsid, ident_name, mimetype, datalen, "
+ + "filename, data) VALUES (?, ?, ?, ?, ?, ?, ?, ?);");
+ stmt.setLong(1,msgid);
+ stmt.setInt(2,partnum);
+ stmt.setInt(3,nsid);
+ stmt.setString(4,name);
+ stmt.setString(5,mimetype);
+ stmt.setInt(6,length);
+ stmt.setString(7,filename);
+ stmt.setBinaryStream(8,data,length);
+ stmt.executeUpdate();
+
+ return partnum;
+
+ } // end try
+ catch (SQLException e)
+ { // translate to a general DatabaseException
+ throw generalException(e);
+
+ } // end catch
+ finally
+ { // shut everything down
+ MySQLUtils.unlockTables(conn);
+ SQLUtils.shutdown(rs);
+ SQLUtils.shutdown(stmt);
+ SQLUtils.shutdown(stmt2);
+ SQLUtils.shutdown(conn);
+
+ } // end finally
+
+ } // end createBinaryPart
+
+ void delete(long msgid) throws DatabaseException
+ {
+ Connection conn = null;
+ PreparedStatement stmt = null;
+ Statement stmt2 = null;
+ try
+ { // get a connection
+ conn = getConnection();
+
+ // lock the tables
+ stmt2 = conn.createStatement();
+ stmt2.executeUpdate("LOCK TABLES us_head WRITE, us_prop WRITE, us_text WRITE, us_text_prop WRITE, "
+ + "us_binary WRITE, us_binary_prop WRITE;");
+
+ // delete all data from the tables
+ stmt = conn.prepareStatement("DELETE FROM us_head WHERE msgid = ?;");
+ stmt.setLong(1,msgid);
+ stmt.executeUpdate();
+ stmt.close();
+ stmt = conn.prepareStatement("DELETE FROM us_prop WHERE msgid = ?;");
+ stmt.setLong(1,msgid);
+ stmt.executeUpdate();
+ stmt.close();
+ stmt = conn.prepareStatement("DELETE FROM us_text WHERE msgid = ?;");
+ stmt.setLong(1,msgid);
+ stmt.executeUpdate();
+ stmt.close();
+ stmt = conn.prepareStatement("DELETE FROM us_text_prop WHERE msgid = ?;");
+ stmt.setLong(1,msgid);
+ stmt.executeUpdate();
+ stmt.close();
+ stmt = conn.prepareStatement("DELETE FROM us_binary WHERE msgid = ?;");
+ stmt.setLong(1,msgid);
+ stmt.executeUpdate();
+ stmt.close();
+ stmt = conn.prepareStatement("DELETE FROM us_binary_prop WHERE msgid = ?;");
+ stmt.setLong(1,msgid);
+ stmt.executeUpdate();
+
+ } // end try
+ catch (SQLException e)
+ { // translate to a general DatabaseException
+ throw generalException(e);
+
+ } // end catch
+ finally
+ { // shut everything down
+ MySQLUtils.unlockTables(conn);
+ SQLUtils.shutdown(stmt);
+ SQLUtils.shutdown(stmt2);
+ SQLUtils.shutdown(conn);
+
+ } // end finally
+
+ } // end delete
+
+ Map listTextParts(long msgid) throws DatabaseException
+ {
+ Connection conn = null;
+ PreparedStatement stmt = null;
+ ResultSet rs = null;
+ try
+ { // get a connection
+ conn = getConnection();
+
+ // run the statement that lists the parts
+ stmt = conn.prepareStatement("SELECT part, ident_nsid, ident_name FROM us_text WHERE msgid = ? ORDER BY part;");
+ stmt.setLong(1,msgid);
+ rs = stmt.executeQuery();
+
+ // create and return the listing
+ TreeMap rc = new TreeMap();
+ while (rs.next())
+ rc.put(new Integer(rs.getInt(1)),new PropertyKey(rs.getInt(2),rs.getString(3)));
+ return rc;
+
+ } // end try
+ catch (SQLException e)
+ { // translate to a general DatabaseException
+ throw generalException(e);
+
+ } // end catch
+ finally
+ { // shut everything down
+ SQLUtils.shutdown(rs);
+ SQLUtils.shutdown(stmt);
+ SQLUtils.shutdown(conn);
+
+ } // end finally
+
+ } // end listTextParts
+
+ Map listBinaryParts(long msgid) throws DatabaseException
+ {
+ Connection conn = null;
+ PreparedStatement stmt = null;
+ ResultSet rs = null;
+ try
+ { // get a connection
+ conn = getConnection();
+
+ // run the statement that lists the parts
+ stmt = conn.prepareStatement("SELECT part, ident_nsid, ident_name FROM us_binary WHERE msgid = ? "
+ + "ORDER BY part;");
+ stmt.setLong(1,msgid);
+ rs = stmt.executeQuery();
+
+ // create and return the listing
+ TreeMap rc = new TreeMap();
+ while (rs.next())
+ rc.put(new Integer(rs.getInt(1)),new PropertyKey(rs.getInt(2),rs.getString(3)));
+ return rc;
+
+ } // end try
+ catch (SQLException e)
+ { // translate to a general DatabaseException
+ throw generalException(e);
+
+ } // end catch
+ finally
+ { // shut everything down
+ SQLUtils.shutdown(rs);
+ SQLUtils.shutdown(stmt);
+ SQLUtils.shutdown(conn);
+
+ } // end finally
+
+ } // end listTextParts
+
} // end class MessageOps_mysql
diff --git a/src/dynamo-framework/com/silverwrist/dynamo/unistore/TextPartImpl.java b/src/dynamo-framework/com/silverwrist/dynamo/unistore/TextPartImpl.java
index 49cce84..20a5e78 100644
--- a/src/dynamo-framework/com/silverwrist/dynamo/unistore/TextPartImpl.java
+++ b/src/dynamo-framework/com/silverwrist/dynamo/unistore/TextPartImpl.java
@@ -21,6 +21,7 @@ import java.io.*;
import java.lang.ref.*;
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.*;
@@ -49,7 +50,7 @@ class TextPartImpl implements UniStoreTextPart
private SoftReference m_text = null;
/*--------------------------------------------------------------------------------
- * Constructor
+ * Constructors
*--------------------------------------------------------------------------------
*/
@@ -76,6 +77,36 @@ class TextPartImpl implements UniStoreTextPart
} // end constructor
+ TextPartImpl(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_linecount = -1;
+ 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 text part)";
+ return "message " + m_parent.getMessageID() + ", text part " + m_identity.toString();
+
+ } // end toString
+
/*--------------------------------------------------------------------------------
* Implementations from interface ObjectProvider
*--------------------------------------------------------------------------------
@@ -90,6 +121,8 @@ class TextPartImpl implements UniStoreTextPart
*/
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);
@@ -141,6 +174,8 @@ class TextPartImpl implements UniStoreTextPart
public Object setObject(DynamoUser caller, String namespace, String name, Object value)
throws DatabaseException, DynamoSecurityException
{
+ if (m_ops==null)
+ throw new DatabaseException(TextPartImpl.class,"UniStoreMessages","part.deleted");
m_parent.testPermission(caller,namespace,"set.property","no.setProperty");
Object rc = null;
// convert the namespace name to an ID here
@@ -174,6 +209,8 @@ class TextPartImpl implements UniStoreTextPart
public Object removeObject(DynamoUser caller, String namespace, String name)
throws DatabaseException, DynamoSecurityException
{
+ if (m_ops==null)
+ throw new DatabaseException(TextPartImpl.class,"UniStoreMessages","part.deleted");
m_parent.testPermission(caller,namespace,"remove.property","no.removeProperty");
Object rc = null;
// convert the namespace name to an ID here
@@ -201,6 +238,9 @@ class TextPartImpl implements UniStoreTextPart
*/
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);
@@ -222,6 +262,9 @@ class TextPartImpl implements UniStoreTextPart
*/
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);
@@ -252,7 +295,7 @@ class TextPartImpl implements UniStoreTextPart
public long getMessageID()
{
- return m_parent.getMessageID();
+ return (m_parent==null) ? -1L : m_parent.getMessageID();
} // end getMessageID
@@ -300,6 +343,8 @@ class TextPartImpl implements UniStoreTextPart
public void touchRead() throws DatabaseException
{
+ if (m_ops==null)
+ throw new DatabaseException(TextPartImpl.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);
@@ -312,6 +357,22 @@ class TextPartImpl implements UniStoreTextPart
} // end touchRead
+ public synchronized void delete(DynamoUser caller) throws DatabaseException, DynamoSecurityException
+ {
+ if (m_ops==null)
+ throw new DatabaseException(TextPartImpl.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.deletedTextPart(m_part,m_identity);
+
+ // Zap it from the database.
+ m_ops.delete(m_parent.getMessageID(),m_part);
+
+ baleeted(); // BALEETED!
+
+ } // end delete
+
/*--------------------------------------------------------------------------------
* Implementations from interface UniStoreTextPart
*--------------------------------------------------------------------------------
@@ -325,6 +386,8 @@ class TextPartImpl implements UniStoreTextPart
public synchronized String getText() throws DatabaseException
{
+ if (m_ops==null)
+ throw new DatabaseException(TextPartImpl.class,"UniStoreMessages","part.deleted");
String rc = null;
if (m_text!=null)
rc = (String)(m_text.get());
@@ -341,8 +404,58 @@ class TextPartImpl implements UniStoreTextPart
public Reader getTextAsReader() throws DatabaseException
{
+ if (m_ops==null)
+ throw new DatabaseException(TextPartImpl.class,"UniStoreMessages","part.deleted");
return new StringReader(this.getText());
} // end getTextAsReader
+ /*--------------------------------------------------------------------------------
+ * External operations
+ *--------------------------------------------------------------------------------
+ */
+
+ void precacheText(String txt)
+ {
+ m_text = new SoftReference(txt);
+
+ } // end txt
+
+ 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.
+ * See this page 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_linecount = -1; + m_nread = 0; + m_lastread = null; + m_properties.clear(); + if (m_text!=null) + m_text.clear(); + m_text = null; + + // Post the "deleted" notification event. + m_post.postUpdate(new MessagePartDeletedEvent(this)); + + // Now cut loose the rest of our data. + m_post = null; + m_parent = null; + m_part = -1; + m_identity = null; + + } // end baleeted + } // end class TextPartImpl diff --git a/src/dynamo-framework/com/silverwrist/dynamo/unistore/TextPartOps.java b/src/dynamo-framework/com/silverwrist/dynamo/unistore/TextPartOps.java index 4983f03..ff810ff 100644 --- a/src/dynamo-framework/com/silverwrist/dynamo/unistore/TextPartOps.java +++ b/src/dynamo-framework/com/silverwrist/dynamo/unistore/TextPartOps.java @@ -55,4 +55,6 @@ abstract class TextPartOps extends OpsBase abstract String getText(long msgid, int part) throws DatabaseException; + abstract void delete(long msgid, int part) throws DatabaseException; + } // end class TextPartOps diff --git a/src/dynamo-framework/com/silverwrist/dynamo/unistore/TextPartOps_mysql.java b/src/dynamo-framework/com/silverwrist/dynamo/unistore/TextPartOps_mysql.java index 46aacd2..cefc6ab 100644 --- a/src/dynamo-framework/com/silverwrist/dynamo/unistore/TextPartOps_mysql.java +++ b/src/dynamo-framework/com/silverwrist/dynamo/unistore/TextPartOps_mysql.java @@ -455,4 +455,57 @@ class TextPartOps_mysql extends TextPartOps } // end getText + void delete(long msgid, int part) throws DatabaseException + { + Connection conn = null; + PreparedStatement stmt = null; + Statement stmt2 = null; + try + { // get a connection + conn = getConnection(); + + // lock the tables + stmt2 = conn.createStatement(); + stmt2.executeUpdate("LOCK TABLES us_text WRITE, us_text_props WRITE;"); + + // delete all entries from the main and property tables + stmt = conn.prepareStatement("DELETE FROM us_text WHERE msgid = ? AND part = ?;"); + stmt.setLong(1,msgid); + stmt.setInt(2,part); + stmt.executeUpdate(); + stmt.close(); + stmt = conn.prepareStatement("DELETE FROM us_text_prop WHERE msgid = ? AND part = ?;"); + stmt.setLong(1,msgid); + stmt.setInt(2,part); + stmt.executeUpdate(); + stmt.close(); + + // renumber all entries above that one to close the gap + stmt = conn.prepareStatement("UPDATE us_text SET part = part - 1 WHERE msgid = ? AND part > ?;"); + stmt.setLong(1,msgid); + stmt.setInt(2,part); + stmt.executeUpdate(); + stmt.close(); + stmt = conn.prepareStatement("UPDATE us_text_prop SET part = part - 1 WHERE msgid = ? AND part > ?;"); + stmt.setLong(1,msgid); + stmt.setInt(2,part); + stmt.executeUpdate(); + + } // end try + catch (SQLException e) + { // translate to a general DatabaseException + throw generalException(e); + + } // end catch + finally + { // shut everything down + MySQLUtils.unlockTables(conn); + SQLUtils.shutdown(stmt); + SQLUtils.shutdown(stmt2); + SQLUtils.shutdown(conn); + + } // end finally + + } // end delete + } // end class TextPartOps_mysql diff --git a/src/dynamo-framework/com/silverwrist/dynamo/unistore/UniStoreManager.java b/src/dynamo-framework/com/silverwrist/dynamo/unistore/UniStoreManager.java index da766e3..2a7da7d 100644 --- a/src/dynamo-framework/com/silverwrist/dynamo/unistore/UniStoreManager.java +++ b/src/dynamo-framework/com/silverwrist/dynamo/unistore/UniStoreManager.java @@ -24,6 +24,7 @@ import com.silverwrist.util.*; import com.silverwrist.util.xml.*; 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; @@ -38,6 +39,9 @@ public class UniStoreManager implements NamedObject, ComponentInitialize, Compon private static Logger logger = Logger.getLogger(UniStoreManager.class); + private static final Long NO_PARENT = new Long(0); + private static final Integer NO_SEQ = new Integer(0); + private static int DEFAULT_MSGCACHE_HARD = 100; private static int DEFAULT_MSGCACHE_SOFT = 1000; @@ -190,9 +194,46 @@ public class UniStoreManager implements NamedObject, ComponentInitialize, Compon m_msgcache.put(key,rc); } // end if + else if (rc.getMessageID()!=msgid) + { // This only happens when the message has been deleted but its object still lingers in the cache. Make + // sure the cache entry is removed and throw an error. + m_msgcache.remove(key); + DatabaseException de = new DatabaseException(UniStoreManager.class,"UniStoreMessages","no.message"); + de.setParameter(0,String.valueOf(msgid)); + throw de; + + } // end else if. return rc; } // end getMessage + public UniStoreMessage createMessage(DynamoUser caller) throws DatabaseException + { + // Call down to the database to create the record. + java.util.Date tick = new java.util.Date(); + long newid = m_ops.createMessage(caller.getUID(),tick); + + // Fake up a parameter buffer and use it to create the MessageImpl object. + Long key = new Long(newid); + HashMap params = new HashMap(); + params.put(ManagerOps.PARAM_MSGID,key); + params.put(ManagerOps.PARAM_PARENT,NO_PARENT); + params.put(ManagerOps.PARAM_SEQ,NO_SEQ); + params.put(ManagerOps.PARAM_CREATOR,new Integer(caller.getUID())); + params.put(ManagerOps.PARAM_POSTED,tick); + MessageImpl rc = new MessageImpl(m_ops.getMessageOps(),m_ns_cache,m_srm,m_users,m_post,params); + rc.zeroCounts(); + + synchronized (this) + { // add the new message object to the cache + m_msgcache.put(key,rc); + + } // end synchronized block + + m_post.postUpdate(new MessageCreatedEvent(rc)); + return rc; + + } // end createMessage + } // end class UniStoreManager diff --git a/src/dynamo-framework/com/silverwrist/dynamo/unistore/UniStoreMessages.properties b/src/dynamo-framework/com/silverwrist/dynamo/unistore/UniStoreMessages.properties index e540dfe..3945167 100644 --- a/src/dynamo-framework/com/silverwrist/dynamo/unistore/UniStoreMessages.properties +++ b/src/dynamo-framework/com/silverwrist/dynamo/unistore/UniStoreMessages.properties @@ -26,3 +26,9 @@ bad.loadText.part=Unable to find text part #{0} of message #{1} in the Universal bad.loadText.id=Unable to find text part with ID {0}::{1} in message #{2} in the Universal Message Store. bad.loadBinary.part=Unable to find binary part #{0} of message #{1} in the Universal Message Store. bad.loadBinary.id=Unable to find binary part with ID {0}::{1} in message #{2} in the Universal Message Store. +no.createPart=You are not authorized to create a new part in message #{0} in the Universal Message Store. +part.exists=A part with this identity already exists in message #{0}. +no.deletePart=You are not authorized to delete a part from message #{0} in the Universal Message Store. +part.deleted=This part has been deleted. +no.deleteMessage=You are not authorized to delete message #{0} in the Universal Message Store. +message.deleted=This message has been deleted.