added message part add/delete and message create/delete to the UniStore

operations
This commit is contained in:
Eric J. Bowersox 2003-06-11 20:24:48 +00:00
parent 5fe6dd78b3
commit 172ec3b314
21 changed files with 1343 additions and 15 deletions

View File

@ -0,0 +1,46 @@
/*
* 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.event;
import com.silverwrist.dynamo.iface.UniStoreMessage;
public class MessageCreatedEvent extends MessageUpdateEvent
{
/*--------------------------------------------------------------------------------
* Constructor
*--------------------------------------------------------------------------------
*/
public MessageCreatedEvent(UniStoreMessage src)
{
super(src);
} // end constructor
/*--------------------------------------------------------------------------------
* Overrides from class MessageUpdateEvent
*--------------------------------------------------------------------------------
*/
public String toString()
{
return "MessageCreatedEvent: new message ID#" + getMessage().getMessageID();
} // end toString
} // end class MessageCreatedEvent

View File

@ -0,0 +1,46 @@
/*
* 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.event;
import com.silverwrist.dynamo.iface.UniStoreMessage;
public class MessageDeletedEvent extends MessageUpdateEvent
{
/*--------------------------------------------------------------------------------
* Constructor
*--------------------------------------------------------------------------------
*/
public MessageDeletedEvent(UniStoreMessage src)
{
super(src);
} // end constructor
/*--------------------------------------------------------------------------------
* Overrides from class MessageUpdateEvent
*--------------------------------------------------------------------------------
*/
public String toString()
{
return "MessageDeletedEvent: deleted message ID#" + getMessage().getMessageID();
} // end toString
} // end class MessageDeletedEvent

View File

@ -0,0 +1,47 @@
/*
* 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.event;
import com.silverwrist.dynamo.iface.UniStorePart;
public class MessagePartAddedEvent extends MessagePartUpdateEvent
{
/*--------------------------------------------------------------------------------
* Constructor
*--------------------------------------------------------------------------------
*/
public MessagePartAddedEvent(UniStorePart src)
{
super(src);
} // end constructor
/*--------------------------------------------------------------------------------
* Overrides from class MessagePartUpdateEvent
*--------------------------------------------------------------------------------
*/
public String toString()
{
return "MessagePartAddedEvent: message ID#" + getMessagePart().getMessageID() + ", new "
+ (isBinaryPart() ? "binary" : "text") + " part " + getMessagePart().getPartIdentity();
} // end toString
} // end class MessagePartAddedEvent

View File

@ -0,0 +1,47 @@
/*
* 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.event;
import com.silverwrist.dynamo.iface.UniStorePart;
public class MessagePartDeletedEvent extends MessagePartUpdateEvent
{
/*--------------------------------------------------------------------------------
* Constructor
*--------------------------------------------------------------------------------
*/
public MessagePartDeletedEvent(UniStorePart src)
{
super(src);
} // end constructor
/*--------------------------------------------------------------------------------
* Overrides from class MessagePartUpdateEvent
*--------------------------------------------------------------------------------
*/
public String toString()
{
return "MessagePartDeletedEvent: message ID#" + getMessagePart().getMessageID() + ", deleted "
+ (isBinaryPart() ? "binary" : "text") + " part " + getMessagePart().getPartIdentity();
} // end toString
} // end class MessagePartDeletedEvent

View File

@ -109,16 +109,16 @@ class RewriteURLs implements HTMLCheckerConfigurator, HTMLRewriter
try try
{ // pre-compile all our regex patterns { // pre-compile all our regex patterns
// recognize: http://<host>[:<port>]... // recognize: http://<host>[:<port>]...
l.add(new Match("^http://[A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)*(:[0-9]+)?(/.*)?")); l.add(new Match("^http://[A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)*(:[0-9]+)?(/.*)?$"));
// recognize: ftp://<host>[:<port>]... // recognize: ftp://<host>[:<port>]...
l.add(new Match("^ftp://[A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)*(:[0-9]+)?(/.*)?")); l.add(new Match("^ftp://[A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)*(:[0-9]+)?(/.*)?$"));
// recognize: gopher://<host>[:<port>]... // recognize: gopher://<host>[:<port>]...
l.add(new Match("^gopher://[A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)*(:[0-9]+)?(/.*)?")); l.add(new Match("^gopher://[A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)*(:[0-9]+)?(/.*)?$"));
// recognize: mailto:<valid E-mail address> // recognize: mailto:<valid E-mail address>[?queryinfo]
l.add(new Match("^mailto:[A-Za-z0-9!#$%*+-/=?^_`{|}~.]+@[A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)*")); l.add(new Match("^mailto:[A-Za-z0-9!#$%*+-/=?^_`{|}~.]+@[A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)*(\\?.+)?$"));
// recognize: news:<newsgroup name> // recognize: news:<newsgroup name>
l.add(new Match("^news:[A-Za-z0-9-_]+(\\.[A-Za-z0-9-_]+)*$")); l.add(new Match("^news:[A-Za-z0-9-_]+(\\.[A-Za-z0-9-_]+)*$"));
@ -137,13 +137,13 @@ class RewriteURLs implements HTMLCheckerConfigurator, HTMLRewriter
l.add(new Match("^tn3270://[A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)*(:[0-9]+)?$")); l.add(new Match("^tn3270://[A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)*(:[0-9]+)?$"));
// recognize "www.whatever" and tack http:// onto the front // recognize "www.whatever" and tack http:// onto the front
l.add(new Match("^www\\.[A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)*(/.*)?","http://")); l.add(new Match("^www\\.[A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)*(/.*)?$","http://"));
// recognize "ftp.whatever" and tack ftp:// onto the front // recognize "ftp.whatever" and tack ftp:// onto the front
l.add(new Match("^ftp\\.[A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)*(/.*)?","ftp://")); l.add(new Match("^ftp\\.[A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)*(/.*)?$","ftp://"));
// recognize "gopher.whatever" and tack gopher:// onto the front // recognize "gopher.whatever" and tack gopher:// onto the front
l.add(new Match("^gopher\\.[A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)*(/.*)?","gopher://")); l.add(new Match("^gopher\\.[A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)*(/.*)?$","gopher://"));
} // end try } // end try
catch (PatternSyntaxException pe) catch (PatternSyntaxException pe)

View File

@ -23,4 +23,6 @@ public interface UniStore
{ {
public UniStoreMessage getMessage(long msgid) throws DatabaseException; public UniStoreMessage getMessage(long msgid) throws DatabaseException;
public UniStoreMessage createMessage(DynamoUser caller) throws DatabaseException;
} // end interface UniStore } // end interface UniStore

View File

@ -17,6 +17,8 @@
*/ */
package com.silverwrist.dynamo.iface; package com.silverwrist.dynamo.iface;
import java.io.InputStream;
import java.io.IOException;
import java.security.acl.AclNotFoundException; import java.security.acl.AclNotFoundException;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
@ -61,4 +63,19 @@ public interface UniStoreMessage extends SecureObjectStore
public List getBinaryParts() throws DatabaseException; public List getBinaryParts() throws DatabaseException;
public UniStoreTextPart createTextPart(DynamoUser caller, String namespace, String name, String mimetype,
HTMLChecker data) throws DatabaseException, DynamoSecurityException;
public UniStoreTextPart createTextPart(DynamoUser caller, String namespace, String name, String mimetype,
String data) throws DatabaseException, DynamoSecurityException;
public UniStoreBinaryPart createBinaryPart(DynamoUser caller, String namespace, String name, DataItem data)
throws IOException, DatabaseException, DynamoSecurityException;
public UniStoreBinaryPart createBinaryPart(DynamoUser caller, String namespace, String name, String mimetype,
String filename, int length, InputStream data)
throws DatabaseException, DynamoSecurityException;
public void delete(DynamoUser caller) throws DatabaseException, DynamoSecurityException;
} // end interface UniStoreMessage } // end interface UniStoreMessage

View File

@ -19,6 +19,7 @@ package com.silverwrist.dynamo.iface;
import java.util.Date; import java.util.Date;
import com.silverwrist.dynamo.except.DatabaseException; import com.silverwrist.dynamo.except.DatabaseException;
import com.silverwrist.dynamo.except.DynamoSecurityException;
import com.silverwrist.dynamo.util.QualifiedNameKey; import com.silverwrist.dynamo.util.QualifiedNameKey;
public interface UniStorePart extends SecureObjectStore public interface UniStorePart extends SecureObjectStore
@ -41,4 +42,6 @@ public interface UniStorePart extends SecureObjectStore
public void touchRead() throws DatabaseException; public void touchRead() throws DatabaseException;
public void delete(DynamoUser caller) throws DatabaseException, DynamoSecurityException;
} // end interface UniStorePart } // end interface UniStorePart

View File

@ -22,6 +22,7 @@ import java.sql.Blob;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.*; import java.util.*;
import org.apache.commons.collections.*; import org.apache.commons.collections.*;
import com.silverwrist.dynamo.Namespaces;
import com.silverwrist.dynamo.db.NamespaceCache; import com.silverwrist.dynamo.db.NamespaceCache;
import com.silverwrist.dynamo.event.*; import com.silverwrist.dynamo.event.*;
import com.silverwrist.dynamo.except.*; import com.silverwrist.dynamo.except.*;
@ -141,7 +142,7 @@ class BinaryPartImpl implements UniStoreBinaryPart
private ReferenceMap m_properties; private ReferenceMap m_properties;
/*-------------------------------------------------------------------------------- /*--------------------------------------------------------------------------------
* Constructor * Constructors
*-------------------------------------------------------------------------------- *--------------------------------------------------------------------------------
*/ */
@ -166,6 +167,36 @@ class BinaryPartImpl implements UniStoreBinaryPart
} // end constructor } // end constructor
BinaryPartImpl(PostDynamicUpdate post, MessageImpl parent, int part, QualifiedNameKey identity)
{
m_ops = null;
m_nscache = null;
m_post = post;
m_parent = parent;
m_part = part;
m_identity = identity;
m_mimetype = null;
m_size = -1;
m_filename = null;
m_nread = 0;
m_lastread = null;
m_properties = new ReferenceMap(ReferenceMap.HARD,ReferenceMap.SOFT);
} // end constructor
/*--------------------------------------------------------------------------------
* Overrides from class Object
*--------------------------------------------------------------------------------
*/
public String toString()
{
if (m_ops==null)
return "(deleted binary part)";
return "message " + m_parent.getMessageID() + ", binary part " + m_identity.toString();
} // end toString
/*-------------------------------------------------------------------------------- /*--------------------------------------------------------------------------------
* Implementations from interface ObjectProvider * Implementations from interface ObjectProvider
*-------------------------------------------------------------------------------- *--------------------------------------------------------------------------------
@ -180,6 +211,8 @@ class BinaryPartImpl implements UniStoreBinaryPart
*/ */
public Object getObject(String namespace, String name) public Object getObject(String namespace, String name)
{ {
if (m_ops==null)
throw new NoSuchObjectException(this.toString(),namespace,name);
try try
{ // convert the namespace name to an ID here { // convert the namespace name to an ID here
PropertyKey key = new PropertyKey(m_nscache.namespaceNameToId(namespace),name); PropertyKey key = new PropertyKey(m_nscache.namespaceNameToId(namespace),name);
@ -231,6 +264,8 @@ class BinaryPartImpl implements UniStoreBinaryPart
public Object setObject(DynamoUser caller, String namespace, String name, Object value) public Object setObject(DynamoUser caller, String namespace, String name, Object value)
throws DatabaseException, DynamoSecurityException throws DatabaseException, DynamoSecurityException
{ {
if (m_ops==null)
throw new DatabaseException(BinaryPartImpl.class,"UniStoreMessages","part.deleted");
m_parent.testPermission(caller,namespace,"set.property","no.setProperty"); m_parent.testPermission(caller,namespace,"set.property","no.setProperty");
Object rc = null; Object rc = null;
// convert the namespace name to an ID here // convert the namespace name to an ID here
@ -264,6 +299,8 @@ class BinaryPartImpl implements UniStoreBinaryPart
public Object removeObject(DynamoUser caller, String namespace, String name) public Object removeObject(DynamoUser caller, String namespace, String name)
throws DatabaseException, DynamoSecurityException throws DatabaseException, DynamoSecurityException
{ {
if (m_ops==null)
throw new DatabaseException(BinaryPartImpl.class,"UniStoreMessages","part.deleted");
m_parent.testPermission(caller,namespace,"remove.property","no.removeProperty"); m_parent.testPermission(caller,namespace,"remove.property","no.removeProperty");
Object rc = null; Object rc = null;
// convert the namespace name to an ID here // convert the namespace name to an ID here
@ -291,6 +328,9 @@ class BinaryPartImpl implements UniStoreBinaryPart
*/ */
public Collection getNamespaces() throws DatabaseException 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 // call through to the database to get the list of namespace IDs
int[] ids = m_ops.getPropertyNamespaceIDs(m_parent.getMessageID(),m_part); int[] ids = m_ops.getPropertyNamespaceIDs(m_parent.getMessageID(),m_part);
@ -312,6 +352,9 @@ class BinaryPartImpl implements UniStoreBinaryPart
*/ */
public Collection getNamesForNamespace(String namespace) throws DatabaseException 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 // call through to the database to get the data for this namespace
int nsid = m_nscache.namespaceNameToId(namespace); int nsid = m_nscache.namespaceNameToId(namespace);
Map data = m_ops.getAllProperties(m_parent.getMessageID(),m_part,nsid); Map data = m_ops.getAllProperties(m_parent.getMessageID(),m_part,nsid);
@ -342,7 +385,7 @@ class BinaryPartImpl implements UniStoreBinaryPart
public long getMessageID() public long getMessageID()
{ {
return m_parent.getMessageID(); return (m_parent==null) ? -1L : m_parent.getMessageID();
} // end getMessageID } // end getMessageID
@ -390,6 +433,8 @@ class BinaryPartImpl implements UniStoreBinaryPart
public void touchRead() throws DatabaseException public void touchRead() throws DatabaseException
{ {
if (m_ops==null)
throw new DatabaseException(BinaryPartImpl.class,"UniStoreMessages","part.deleted");
synchronized (this) synchronized (this)
{ // touch the database, then the local values { // touch the database, then the local values
java.util.Date tmp = m_ops.touchRead(m_parent.getMessageID(),m_part); java.util.Date tmp = m_ops.touchRead(m_parent.getMessageID(),m_part);
@ -402,6 +447,22 @@ class BinaryPartImpl implements UniStoreBinaryPart
} // end touchRead } // end touchRead
public synchronized void delete(DynamoUser caller) throws DatabaseException, DynamoSecurityException
{
if (m_ops==null)
throw new DatabaseException(BinaryPartImpl.class,"UniStoreMessages","part.deleted");
m_parent.testPermission(caller,Namespaces.UNISTORE_PERMISSIONS_NAMESPACE,"delete.part","no.deletePart");
// Cut this object loose from the parent.
m_parent.deletedBinaryPart(m_part,m_identity);
// Zap it from the database.
m_ops.delete(m_parent.getMessageID(),m_part);
baleeted(); // BALEETED!
} // end delete
/*-------------------------------------------------------------------------------- /*--------------------------------------------------------------------------------
* Implementations from interface DataItem * Implementations from interface DataItem
*-------------------------------------------------------------------------------- *--------------------------------------------------------------------------------
@ -415,6 +476,14 @@ class BinaryPartImpl implements UniStoreBinaryPart
public InputStream getDataStream() throws IOException public InputStream getDataStream() throws IOException
{ {
if (m_ops==null)
{ // we've been deleted!
IOException ioe = new IOException("Part has been deleted");
ioe.initCause(new DatabaseException(BinaryPartImpl.class,"UniStoreMessages","part.deleted"));
throw ioe;
} // end if
try try
{ // call through to the database to get the data stream { // call through to the database to get the data stream
return m_ops.getData(m_parent.getMessageID(),m_part); return m_ops.getData(m_parent.getMessageID(),m_part);
@ -432,6 +501,14 @@ class BinaryPartImpl implements UniStoreBinaryPart
public Blob getBlob() throws SQLException public Blob getBlob() throws SQLException
{ {
if (m_ops==null)
{ // we've been deleted!
SQLException se = new SQLException("Part has been deleted");
se.initCause(new DatabaseException(BinaryPartImpl.class,"UniStoreMessages","part.deleted"));
throw se;
} // end if
try try
{ // load the data from the database and create the Blob around it { // load the data from the database and create the Blob around it
byte[] data = new byte[m_size]; byte[] data = new byte[m_size];
@ -461,4 +538,43 @@ class BinaryPartImpl implements UniStoreBinaryPart
*-------------------------------------------------------------------------------- *--------------------------------------------------------------------------------
*/ */
/*--------------------------------------------------------------------------------
* External operations
*--------------------------------------------------------------------------------
*/
void resetPartNumber(int new_num)
{
m_part = new_num;
} // end resetPartNumber
/**
* Called after the part has been deleted, either alone or through the entire message being deleted. This
* method nulls out the internal data of the object and posts a "part-deleted" notification.<P>
* See <A HREF="http://www.homestarrunner.com/sbemail50.html">this page</A> for the source of the method name.
*/
synchronized void baleeted()
{
// Cut loose most of our data before we post an update event.
m_ops = null;
m_nscache = null;
m_mimetype = null;
m_size = -1;
m_filename = null;
m_nread = 0;
m_lastread = null;
m_properties.clear();
// Post the "deleted" notification event.
m_post.postUpdate(new MessagePartDeletedEvent(this));
// Cut loose the rest of our data.
m_post = null;
m_parent = null;
m_part = -1;
m_identity = null;
} // end baleeted
} // end class BinaryPartImpl } // end class BinaryPartImpl

View File

@ -59,4 +59,6 @@ abstract class BinaryPartOps extends OpsBase
abstract int getData(long msgid, int part, byte[] here) throws IOException, DatabaseException; abstract int getData(long msgid, int part, byte[] here) throws IOException, DatabaseException;
abstract void delete(long msgid, int part) throws DatabaseException;
} // end class BinaryPartOps } // end class BinaryPartOps

View File

@ -503,4 +503,57 @@ class BinaryPartOps_mysql extends BinaryPartOps
} // end getData } // 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 } // end class BinaryPartOps_mysql

View File

@ -77,6 +77,8 @@ abstract class ManagerOps extends OpsBase
abstract Map getMessageData(long msgid) throws DatabaseException; abstract Map getMessageData(long msgid) throws DatabaseException;
abstract long createMessage(int creator, java.util.Date postdate) throws DatabaseException;
/*-------------------------------------------------------------------------------- /*--------------------------------------------------------------------------------
* External operations * External operations
*-------------------------------------------------------------------------------- *--------------------------------------------------------------------------------

View File

@ -116,4 +116,43 @@ public class ManagerOps_mysql extends ManagerOps
} // end getMessageData } // 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 } // end class ManagerOps_mysql

View File

@ -17,8 +17,10 @@
*/ */
package com.silverwrist.dynamo.unistore; package com.silverwrist.dynamo.unistore;
import java.io.*;
import java.security.acl.AclNotFoundException; import java.security.acl.AclNotFoundException;
import java.util.*; import java.util.*;
import java.util.regex.*;
import org.apache.commons.collections.*; import org.apache.commons.collections.*;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
import com.silverwrist.dynamo.Namespaces; import com.silverwrist.dynamo.Namespaces;
@ -39,6 +41,10 @@ class MessageImpl implements UniStoreMessage
private static Logger logger = Logger.getLogger(MessageImpl.class); private static Logger logger = Logger.getLogger(MessageImpl.class);
private static final Integer NO_READS = new Integer(0);
private static Pattern NEWLINES;
/*-------------------------------------------------------------------------------- /*--------------------------------------------------------------------------------
* Attributes * Attributes
*-------------------------------------------------------------------------------- *--------------------------------------------------------------------------------
@ -92,13 +98,109 @@ class MessageImpl implements UniStoreMessage
} // end constructor } // 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 * Implementations from interface ObjectProvider
*-------------------------------------------------------------------------------- *--------------------------------------------------------------------------------
*/ */
/** /**
* Retrieves an object from this <CODE>ObjectProvider</CODE>. * Retrieves an object from this message's properties.
* *
* @param namespace The namespace to interpret the name relative to. * @param namespace The namespace to interpret the name relative to.
* @param name The name of the object to be retrieved. * @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) public Object getObject(String namespace, String name)
{ {
if (m_ops==null)
throw new NoSuchObjectException(this.toString(),namespace,name);
try try
{ // convert the namespace name to an ID here { // convert the namespace name to an ID here
PropertyKey key = new PropertyKey(m_nscache.namespaceNameToId(namespace),name); 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) public Object setObject(DynamoUser caller, String namespace, String name, Object value)
throws DatabaseException, DynamoSecurityException throws DatabaseException, DynamoSecurityException
{ {
if (m_ops==null)
throw new DatabaseException(MessageImpl.class,"UniStoreMessages","message.deleted");
testPermission(caller,namespace,"set.property","no.setProperty"); testPermission(caller,namespace,"set.property","no.setProperty");
Object rc = null; Object rc = null;
// convert the namespace name to an ID here // 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) public Object removeObject(DynamoUser caller, String namespace, String name)
throws DatabaseException, DynamoSecurityException throws DatabaseException, DynamoSecurityException
{ {
if (m_ops==null)
throw new DatabaseException(MessageImpl.class,"UniStoreMessages","message.deleted");
testPermission(caller,namespace,"remove.property","no.removeProperty"); testPermission(caller,namespace,"remove.property","no.removeProperty");
Object rc = null; Object rc = null;
// convert the namespace name to an ID here // convert the namespace name to an ID here
@ -217,6 +326,9 @@ class MessageImpl implements UniStoreMessage
*/ */
public Collection getNamespaces() throws DatabaseException 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 // call through to the database to get the list of namespace IDs
int[] ids = m_ops.getPropertyNamespaceIDs(m_id); int[] ids = m_ops.getPropertyNamespaceIDs(m_id);
@ -238,6 +350,9 @@ class MessageImpl implements UniStoreMessage
*/ */
public Collection getNamesForNamespace(String namespace) throws DatabaseException 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 // call through to the database to get the data for this namespace
int nsid = m_nscache.namespaceNameToId(namespace); int nsid = m_nscache.namespaceNameToId(namespace);
Map data = m_ops.getAllProperties(m_id,nsid); Map data = m_ops.getAllProperties(m_id,nsid);
@ -320,7 +435,9 @@ class MessageImpl implements UniStoreMessage
public DynamoUser getCreator() throws DatabaseException 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 } // end getCreator
@ -332,7 +449,12 @@ class MessageImpl implements UniStoreMessage
public DynamoAcl getAcl() throws DatabaseException, AclNotFoundException 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 } // end getAcl
@ -355,6 +477,8 @@ class MessageImpl implements UniStoreMessage
public synchronized int getNumTextParts() throws DatabaseException public synchronized int getNumTextParts() throws DatabaseException
{ {
if (m_ops==null)
throw new DatabaseException(MessageImpl.class,"UniStoreMessages","message.deleted");
if (m_text_count<0) if (m_text_count<0)
m_text_count = m_ops.getNumTextParts(m_id); m_text_count = m_ops.getNumTextParts(m_id);
return m_text_count; return m_text_count;
@ -363,6 +487,8 @@ class MessageImpl implements UniStoreMessage
public synchronized int getNumBinaryParts() throws DatabaseException public synchronized int getNumBinaryParts() throws DatabaseException
{ {
if (m_ops==null)
throw new DatabaseException(MessageImpl.class,"UniStoreMessages","message.deleted");
if (m_binary_count<0) if (m_binary_count<0)
m_binary_count = m_ops.getNumBinaryParts(m_id); m_binary_count = m_ops.getNumBinaryParts(m_id);
return m_binary_count; return m_binary_count;
@ -371,6 +497,8 @@ class MessageImpl implements UniStoreMessage
public UniStoreTextPart getTextPart(int index) throws DatabaseException public UniStoreTextPart getTextPart(int index) throws DatabaseException
{ {
if (m_ops==null)
throw new DatabaseException(MessageImpl.class,"UniStoreMessages","message.deleted");
Integer key = new Integer(index); Integer key = new Integer(index);
TextPartImpl rc = null; TextPartImpl rc = null;
synchronized (this) synchronized (this)
@ -408,6 +536,8 @@ class MessageImpl implements UniStoreMessage
public UniStoreTextPart getTextPart(String namespace, String name) throws DatabaseException 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); PropertyKey key = new PropertyKey(m_nscache.namespaceNameToId(namespace),name);
TextPartImpl rc = null; TextPartImpl rc = null;
synchronized (this) synchronized (this)
@ -440,6 +570,8 @@ class MessageImpl implements UniStoreMessage
public UniStoreBinaryPart getBinaryPart(int index) throws DatabaseException public UniStoreBinaryPart getBinaryPart(int index) throws DatabaseException
{ {
if (m_ops==null)
throw new DatabaseException(MessageImpl.class,"UniStoreMessages","message.deleted");
Integer key = new Integer(index); Integer key = new Integer(index);
BinaryPartImpl rc = null; BinaryPartImpl rc = null;
synchronized (this) synchronized (this)
@ -477,6 +609,8 @@ class MessageImpl implements UniStoreMessage
public UniStoreBinaryPart getBinaryPart(String namespace, String name) throws DatabaseException 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); PropertyKey key = new PropertyKey(m_nscache.namespaceNameToId(namespace),name);
BinaryPartImpl rc = null; BinaryPartImpl rc = null;
synchronized (this) synchronized (this)
@ -509,6 +643,8 @@ class MessageImpl implements UniStoreMessage
public List getTextParts() throws DatabaseException public List getTextParts() throws DatabaseException
{ {
if (m_ops==null)
throw new DatabaseException(MessageImpl.class,"UniStoreMessages","message.deleted");
int n = this.getNumTextParts(); int n = this.getNumTextParts();
ArrayList rc = new ArrayList(n); ArrayList rc = new ArrayList(n);
for (int i=1; i<=n; i++) for (int i=1; i<=n; i++)
@ -519,6 +655,8 @@ class MessageImpl implements UniStoreMessage
public List getBinaryParts() throws DatabaseException public List getBinaryParts() throws DatabaseException
{ {
if (m_ops==null)
throw new DatabaseException(MessageImpl.class,"UniStoreMessages","message.deleted");
int n = this.getNumBinaryParts(); int n = this.getNumBinaryParts();
ArrayList rc = new ArrayList(n); ArrayList rc = new ArrayList(n);
for (int i=1; i<=n; i++) for (int i=1; i<=n; i++)
@ -527,6 +665,140 @@ class MessageImpl implements UniStoreMessage
} // end getBinaryParts } // 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 * External operations
*-------------------------------------------------------------------------------- *--------------------------------------------------------------------------------
@ -535,6 +807,8 @@ class MessageImpl implements UniStoreMessage
void testPermission(DynamoUser caller, String perm_namespace, String perm_name, String fail_message) void testPermission(DynamoUser caller, String perm_namespace, String perm_name, String fail_message)
throws DatabaseException, DynamoSecurityException throws DatabaseException, DynamoSecurityException
{ {
if (m_ops==null)
throw new DatabaseException(MessageImpl.class,"UniStoreMessages","message.deleted");
if (caller.equals(m_srm.getAdminUser())) if (caller.equals(m_srm.getAdminUser()))
return; // Administrator can do anything return; // Administrator can do anything
if (m_aclid==-1) if (m_aclid==-1)
@ -568,4 +842,127 @@ class MessageImpl implements UniStoreMessage
} // end testPermission } // 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 } // end class MessageImpl

View File

@ -17,6 +17,7 @@
*/ */
package com.silverwrist.dynamo.unistore; package com.silverwrist.dynamo.unistore;
import java.io.*;
import java.util.*; import java.util.*;
import com.silverwrist.dynamo.db.OpsBase; import com.silverwrist.dynamo.db.OpsBase;
import com.silverwrist.dynamo.except.*; import com.silverwrist.dynamo.except.*;
@ -112,6 +113,18 @@ abstract class MessageOps extends OpsBase
abstract Map loadBinaryPart(long msgid, PropertyKey identity) throws DatabaseException; 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 * External operations
*-------------------------------------------------------------------------------- *--------------------------------------------------------------------------------

View File

@ -17,6 +17,7 @@
*/ */
package com.silverwrist.dynamo.unistore; package com.silverwrist.dynamo.unistore;
import java.io.*;
import java.sql.*; import java.sql.*;
import java.util.*; import java.util.*;
import com.silverwrist.util.*; import com.silverwrist.util.*;
@ -790,4 +791,286 @@ class MessageOps_mysql extends MessageOps
} // end loadBinaryPart } // 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 } // end class MessageOps_mysql

View File

@ -21,6 +21,7 @@ import java.io.*;
import java.lang.ref.*; import java.lang.ref.*;
import java.util.*; import java.util.*;
import org.apache.commons.collections.*; import org.apache.commons.collections.*;
import com.silverwrist.dynamo.Namespaces;
import com.silverwrist.dynamo.db.NamespaceCache; import com.silverwrist.dynamo.db.NamespaceCache;
import com.silverwrist.dynamo.event.*; import com.silverwrist.dynamo.event.*;
import com.silverwrist.dynamo.except.*; import com.silverwrist.dynamo.except.*;
@ -49,7 +50,7 @@ class TextPartImpl implements UniStoreTextPart
private SoftReference m_text = null; private SoftReference m_text = null;
/*-------------------------------------------------------------------------------- /*--------------------------------------------------------------------------------
* Constructor * Constructors
*-------------------------------------------------------------------------------- *--------------------------------------------------------------------------------
*/ */
@ -76,6 +77,36 @@ class TextPartImpl implements UniStoreTextPart
} // end constructor } // 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 * Implementations from interface ObjectProvider
*-------------------------------------------------------------------------------- *--------------------------------------------------------------------------------
@ -90,6 +121,8 @@ class TextPartImpl implements UniStoreTextPart
*/ */
public Object getObject(String namespace, String name) public Object getObject(String namespace, String name)
{ {
if (m_ops==null)
throw new NoSuchObjectException(this.toString(),namespace,name);
try try
{ // convert the namespace name to an ID here { // convert the namespace name to an ID here
PropertyKey key = new PropertyKey(m_nscache.namespaceNameToId(namespace),name); 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) public Object setObject(DynamoUser caller, String namespace, String name, Object value)
throws DatabaseException, DynamoSecurityException throws DatabaseException, DynamoSecurityException
{ {
if (m_ops==null)
throw new DatabaseException(TextPartImpl.class,"UniStoreMessages","part.deleted");
m_parent.testPermission(caller,namespace,"set.property","no.setProperty"); m_parent.testPermission(caller,namespace,"set.property","no.setProperty");
Object rc = null; Object rc = null;
// convert the namespace name to an ID here // 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) public Object removeObject(DynamoUser caller, String namespace, String name)
throws DatabaseException, DynamoSecurityException throws DatabaseException, DynamoSecurityException
{ {
if (m_ops==null)
throw new DatabaseException(TextPartImpl.class,"UniStoreMessages","part.deleted");
m_parent.testPermission(caller,namespace,"remove.property","no.removeProperty"); m_parent.testPermission(caller,namespace,"remove.property","no.removeProperty");
Object rc = null; Object rc = null;
// convert the namespace name to an ID here // convert the namespace name to an ID here
@ -201,6 +238,9 @@ class TextPartImpl implements UniStoreTextPart
*/ */
public Collection getNamespaces() throws DatabaseException 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 // call through to the database to get the list of namespace IDs
int[] ids = m_ops.getPropertyNamespaceIDs(m_parent.getMessageID(),m_part); 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 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 // call through to the database to get the data for this namespace
int nsid = m_nscache.namespaceNameToId(namespace); int nsid = m_nscache.namespaceNameToId(namespace);
Map data = m_ops.getAllProperties(m_parent.getMessageID(),m_part,nsid); Map data = m_ops.getAllProperties(m_parent.getMessageID(),m_part,nsid);
@ -252,7 +295,7 @@ class TextPartImpl implements UniStoreTextPart
public long getMessageID() public long getMessageID()
{ {
return m_parent.getMessageID(); return (m_parent==null) ? -1L : m_parent.getMessageID();
} // end getMessageID } // end getMessageID
@ -300,6 +343,8 @@ class TextPartImpl implements UniStoreTextPart
public void touchRead() throws DatabaseException public void touchRead() throws DatabaseException
{ {
if (m_ops==null)
throw new DatabaseException(TextPartImpl.class,"UniStoreMessages","part.deleted");
synchronized (this) synchronized (this)
{ // touch the database, then the local values { // touch the database, then the local values
java.util.Date tmp = m_ops.touchRead(m_parent.getMessageID(),m_part); java.util.Date tmp = m_ops.touchRead(m_parent.getMessageID(),m_part);
@ -312,6 +357,22 @@ class TextPartImpl implements UniStoreTextPart
} // end touchRead } // 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 * Implementations from interface UniStoreTextPart
*-------------------------------------------------------------------------------- *--------------------------------------------------------------------------------
@ -325,6 +386,8 @@ class TextPartImpl implements UniStoreTextPart
public synchronized String getText() throws DatabaseException public synchronized String getText() throws DatabaseException
{ {
if (m_ops==null)
throw new DatabaseException(TextPartImpl.class,"UniStoreMessages","part.deleted");
String rc = null; String rc = null;
if (m_text!=null) if (m_text!=null)
rc = (String)(m_text.get()); rc = (String)(m_text.get());
@ -341,8 +404,58 @@ class TextPartImpl implements UniStoreTextPart
public Reader getTextAsReader() throws DatabaseException public Reader getTextAsReader() throws DatabaseException
{ {
if (m_ops==null)
throw new DatabaseException(TextPartImpl.class,"UniStoreMessages","part.deleted");
return new StringReader(this.getText()); return new StringReader(this.getText());
} // end getTextAsReader } // 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.<P>
* See <A HREF="http://www.homestarrunner.com/sbemail50.html">this page</A> for the source of the method name.
*/
synchronized void baleeted()
{
// Cut loose most of our data before we post an update event.
m_ops = null;
m_nscache = null;
m_mimetype = null;
m_size = -1;
m_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 } // end class TextPartImpl

View File

@ -55,4 +55,6 @@ abstract class TextPartOps extends OpsBase
abstract String getText(long msgid, int part) throws DatabaseException; abstract String getText(long msgid, int part) throws DatabaseException;
abstract void delete(long msgid, int part) throws DatabaseException;
} // end class TextPartOps } // end class TextPartOps

View File

@ -455,4 +455,57 @@ class TextPartOps_mysql extends TextPartOps
} // end getText } // 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 } // end class TextPartOps_mysql

View File

@ -24,6 +24,7 @@ import com.silverwrist.util.*;
import com.silverwrist.util.xml.*; import com.silverwrist.util.xml.*;
import com.silverwrist.dynamo.db.NamespaceCache; import com.silverwrist.dynamo.db.NamespaceCache;
import com.silverwrist.dynamo.db.UserManagement; import com.silverwrist.dynamo.db.UserManagement;
import com.silverwrist.dynamo.event.*;
import com.silverwrist.dynamo.except.*; import com.silverwrist.dynamo.except.*;
import com.silverwrist.dynamo.iface.*; import com.silverwrist.dynamo.iface.*;
import com.silverwrist.dynamo.security.SecurityReferenceMonitor; 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 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_HARD = 100;
private static int DEFAULT_MSGCACHE_SOFT = 1000; private static int DEFAULT_MSGCACHE_SOFT = 1000;
@ -190,9 +194,46 @@ public class UniStoreManager implements NamedObject, ComponentInitialize, Compon
m_msgcache.put(key,rc); m_msgcache.put(key,rc);
} // end if } // 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; return rc;
} // end getMessage } // 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 } // end class UniStoreManager

View File

@ -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.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.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. 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.