diff --git a/conf-sso/dynamo.xml b/conf-sso/dynamo.xml index 5dbb8f4..5dcce42 100644 --- a/conf-sso/dynamo.xml +++ b/conf-sso/dynamo.xml @@ -66,6 +66,8 @@ srm data + nscache + users diff --git a/conf/dynamo-venice.xml b/conf/dynamo-venice.xml index 37b6eef..f38ea6d 100644 --- a/conf/dynamo-venice.xml +++ b/conf/dynamo-venice.xml @@ -66,6 +66,8 @@ srm data + nscache + users diff --git a/src/conferencing-module/com/silverwrist/venice/conf/impl/ConferenceImpl.java b/src/conferencing-module/com/silverwrist/venice/conf/impl/ConferenceImpl.java index e65b249..231327d 100644 --- a/src/conferencing-module/com/silverwrist/venice/conf/impl/ConferenceImpl.java +++ b/src/conferencing-module/com/silverwrist/venice/conf/impl/ConferenceImpl.java @@ -19,45 +19,157 @@ package com.silverwrist.venice.conf.impl; import java.security.acl.*; import java.util.*; +import org.apache.log4j.Logger; +import org.apache.commons.collections.*; +import com.silverwrist.dynamo.db.NamespaceCache; +import com.silverwrist.dynamo.db.UserManagement; import com.silverwrist.dynamo.iface.*; import com.silverwrist.dynamo.except.*; +import com.silverwrist.dynamo.security.SecurityReferenceMonitor; +import com.silverwrist.dynamo.util.*; +import com.silverwrist.venice.conf.ConfNamespaces; import com.silverwrist.venice.conf.iface.*; import com.silverwrist.venice.conf.obj.*; class ConferenceImpl implements VeniceConference { + /*-------------------------------------------------------------------------------- + * Static data members + *-------------------------------------------------------------------------------- + */ + + private static Logger logger = Logger.getLogger(ConferenceImpl.class); + /*-------------------------------------------------------------------------------- * Attributes *-------------------------------------------------------------------------------- */ - private DynamicImplConference m_dobj; - private ConferenceOps m_ops; - private int m_confid; - private java.util.Date m_createdate; - private java.util.Date m_lastupdate; - private int m_hosts_gid; - private int m_aclid; - private String m_name; + private DynamicImplConference m_dobj; // used to implement DynamicObject methods + private ConferenceOps m_ops; // conference database operations + private NamespaceCache m_nscache; // namespace cache object + private SecurityReferenceMonitor m_srm; // security reference monitor + private UserManagement m_users; // user management object + private int m_id; // conference ID + private java.util.Date m_createdate; // creation date + private java.util.Date m_lastupdate; // last update date + private int m_hosts_gid; // GID of community hosts group + private int m_aclid; // ACL ID + private String m_name; // conference name + private ReferenceMap m_properties; // properties cache + private String m_prime_alias = null; // primary alias /*-------------------------------------------------------------------------------- * Constructor *-------------------------------------------------------------------------------- */ - ConferenceImpl(ConferenceOps ops, Map params) + ConferenceImpl(ConferenceOps ops, NamespaceCache nscache, SecurityReferenceMonitor srm, UserManagement users, + Map params) { m_dobj = new DynamicImplConference(this); m_ops = ops; - m_confid = ((Integer)(params.get(ConferenceManagerOps.KEY_CONFID))).intValue(); + m_nscache = nscache; + m_srm = srm; + m_users = users; + m_id = ((Integer)(params.get(ConferenceManagerOps.KEY_CONFID))).intValue(); m_createdate = (java.util.Date)(params.get(ConferenceManagerOps.KEY_CREATE_DATE)); m_lastupdate = (java.util.Date)(params.get(ConferenceManagerOps.KEY_LAST_UPDATE)); m_hosts_gid = ((Integer)(params.get(ConferenceManagerOps.KEY_HOSTS_GID))).intValue(); m_aclid = ((Integer)(params.get(ConferenceManagerOps.KEY_ACLID))).intValue(); m_name = (String)(params.get(ConferenceManagerOps.KEY_NAME)); + m_properties = new ReferenceMap(ReferenceMap.HARD,ReferenceMap.SOFT); } // end constructor + /*-------------------------------------------------------------------------------- + * Internal operations + *-------------------------------------------------------------------------------- + */ + + /** + * Performs a permission check on the conference's ACL, as well as the global ACL. Returns without + * incident if the permission check succeeds; throws a + * {@link com.silverwrist.dynamo.except.DynamoSecurityException DynamoSecurityException} if it fails. + * + * @param caller The user making the call, to test the permission. + * @param perm_namespace The namespace of the permission to be tested. + * @param perm_name The name of the permission to be tested. + * @param fail_message The message to be thrown as a DynamoSecurityException if the test fails. + * Interpreted as a message identifier within the ConferenceMessages resource + * bundle, with one parameter: the name of the conference. + * @exception com.silverwrist.dynamo.except.DatabaseException If there was a database failure testing the permission. + * @exception com.silverwrist.dynamo.except.DynamoSecurityException If the permission test failed. + */ + private final void testPermission(DynamoUser caller, String perm_namespace, String perm_name, + String fail_message) throws DatabaseException, DynamoSecurityException + { + try + { // perform security tests... + if (caller.equals(m_srm.getAdminUser())) + return; // Administrator can do anything + if (m_srm.getAcl(m_aclid).testPermission(caller,perm_namespace,perm_name)) + return; // we have the right permission in the conference ACL + if (m_srm.getGlobalAcl().testPermission(caller,perm_namespace,perm_name)) + return; // or we can be authorized by the global ACL + + } // end try + catch (AclNotFoundException e) + { // just fall through and throw the exception + } // end catch + + // throw the DynamoSecurityException + DynamoSecurityException d = new DynamoSecurityException(ConferenceImpl.class,"ConferenceMessages",fail_message); + d.setParameter(0,m_name); + throw d; + + } // end testPermission + + /*-------------------------------------------------------------------------------- + * Overrides from class Object + *-------------------------------------------------------------------------------- + */ + + /** + * Indicates whether some other object is "equal to" this one. + * + * @param o The reference object with which to compare. + * @return true if this object is the same as the o argument; false otherwise. + */ + public boolean equals(Object o) + { + if (o==null) + return false; + if (o instanceof VeniceConference) + return (((VeniceConference)o).getConfID()==m_id); + return false; + + } // end equals + + /** + * Returns a hash code value for the object. This method is supported for the benefit of hashtables such as those + * provided by {@link java.util.Hashtable Hashtable}. + * + * @return A hash code value for this object. + */ + public int hashCode() + { + return m_id; + + } // end hashCode + + /** + * Returns a string representation of the object. In general, the toString method returns a string + * that "textually represents" this object. + * + * @return A string representation of the object. + */ + public String toString() + { + return "Conference \"" + m_name + "\""; + + } // end toString + /*-------------------------------------------------------------------------------- * Implementations from interface DynamicObject *-------------------------------------------------------------------------------- @@ -123,9 +235,49 @@ class ConferenceImpl implements VeniceConference */ public Object getObject(String namespace, String name) { - return null; // TEMP + if (logger.isDebugEnabled()) + logger.debug("Conference.getObject: namespace = " + namespace + ", name = " + name); + try + { // convert the namespace name to an ID here + PropertyKey key = new PropertyKey(m_nscache.namespaceNameToId(namespace),name); + Object rc = null; + synchronized (this) + { // start by looking in the properties map + rc = m_properties.get(key); + if (rc==null) + { // no use - need to try the database + rc = m_ops.getProperty(m_id,key); + if (rc!=null) + { // found in the database + m_properties.put(key,rc); + logger.debug("value found in database"); - } + } // end if + + } // end if + else + logger.debug("value found in cache"); + + } // end synchronized block + + if (rc==null) + { // the object was not found + logger.debug("value not found"); + throw new NoSuchObjectException(this.toString(),namespace,name); + + } // end if + + return rc; + + } // end try + catch (DatabaseException e) + { // translate into our NoSuchObjectException but retain the DatabaseException + logger.debug("Database exception while doing find",e); + throw new NoSuchObjectException(this.toString(),namespace,name,e); + + } // end catch + + } // end getObject /*-------------------------------------------------------------------------------- * Implementations from interface SecureObjectStore @@ -148,9 +300,27 @@ class ConferenceImpl implements VeniceConference public Object setObject(DynamoUser caller, String namespace, String name, Object value) throws DatabaseException, DynamoSecurityException { - return null; // TEMP + testPermission(caller,namespace,"set.property","auth.setProperty"); + DateObjectPair rc = null; - } + // convert the namespace name to an ID here + PropertyKey key = new PropertyKey(m_nscache.namespaceNameToId(namespace),name); + synchronized (this) + { // start by setting the database value + rc = m_ops.setProperty(m_id,key,value); + + // and cache it, too + m_properties.put(key,value); + + // and save off the update date/time + m_lastupdate = rc.getDate(); + + } // end synchronized block + + // TODO: m_post.postUpdate(new CommunityPropertyUpdateEvent(this,namespace,name)); + return rc.getObject(); + + } // end setObject /** * Removes an object from this SecureObjectStore. @@ -167,9 +337,27 @@ class ConferenceImpl implements VeniceConference public Object removeObject(DynamoUser caller, String namespace, String name) throws DatabaseException, DynamoSecurityException { - return null; // TEMP + testPermission(caller,namespace,"remove.property","auth.removeProperty"); + DateObjectPair rc = null; - } + // convert the namespace name to an ID here + PropertyKey key = new PropertyKey(m_nscache.namespaceNameToId(namespace),name); + synchronized (this) + { // start by killing the database value + rc = m_ops.removeProperty(m_id,key); + + // and remove the cached value, too + m_properties.remove(key); + + if (rc!=null) // save off update date.time + m_lastupdate = rc.getDate(); + + } // end synchronized block + + // TODO: m_post.postUpdate(new CommunityPropertyUpdateEvent(this,namespace,name)); + return (rc==null) ? null : rc.getObject(); + + } // end removeObject /** * Returns a collection of all object namespaces that have been set into this SecureObjectStore. @@ -180,9 +368,15 @@ class ConferenceImpl implements VeniceConference */ public Collection getNamespaces() throws DatabaseException { - return null; // TEMP + // call through to the database to get the list of namespace IDs + int[] ids = m_ops.getPropertyNamespaceIDs(m_id); - } + ArrayList rc = new ArrayList(ids.length); + for (int i=0; iSecureObjectStore under @@ -195,9 +389,28 @@ class ConferenceImpl implements VeniceConference */ public Collection getNamesForNamespace(String namespace) throws DatabaseException { - return null; // TEMP + // call through to the database to get the data for this namespace + int nsid = m_nscache.namespaceNameToId(namespace); + Map data = m_ops.getAllProperties(m_id,nsid); - } + // we both create the return value and cache the data values + ArrayList rc = new ArrayList(data.size()); + synchronized (this) + { // do the transfer... + Iterator it = data.entrySet().iterator(); + while (it.hasNext()) + { // copy one entry at a time + Map.Entry ntry = (Map.Entry)(it.next()); + rc.add(ntry.getKey().toString()); + m_properties.put(new PropertyKey(nsid,ntry.getKey().toString()),ntry.getValue()); + + } // end while + + } // end synchronized block + + return Collections.unmodifiableList(rc); + + } // end getNamesForNamespace /*-------------------------------------------------------------------------------- * Implementations from interface VeniceConference @@ -206,13 +419,22 @@ class ConferenceImpl implements VeniceConference public int getConfID() { - return m_confid; + return m_id; } // end getConfID public void setName(DynamoUser caller, String name) throws DatabaseException, DynamoSecurityException { - } + testPermission(caller,ConfNamespaces.PERMISSIONS_NAMESPACE,"set.name","auth.setName"); + synchronized (this) + { // update the name + java.util.Date updated = m_ops.setName(m_id,name); + m_name = name; + m_lastupdate = updated; + + } // end synchronized block + + } // end setName public java.util.Date getCreatedDate() { @@ -226,10 +448,13 @@ class ConferenceImpl implements VeniceConference } // end getLastUpdateDate - public void setLastUpdateDate(DynamoUser caller, java.util.Date date) + public synchronized void setLastUpdateDate(DynamoUser caller, java.util.Date date) throws DatabaseException, DynamoSecurityException { - } + m_ops.setLastUpdateDate(m_id,date); + m_lastupdate = date; + + } // end setLastUpdateDate public int getHostsGID() { @@ -239,38 +464,68 @@ class ConferenceImpl implements VeniceConference public DynamoGroup getHosts() throws DatabaseException { - return null; // TEMP + return m_users.getGroup(m_hosts_gid); - } + } // end getHosts public DynamoAcl getAcl() throws DatabaseException, AclNotFoundException { - return null; // TEMP + return m_srm.getAcl(m_aclid); - } + } // end getAcl public Set getAliases() throws DatabaseException { - return null; // TEMP + return m_ops.getAliases(m_id); - } + } // end getAliases - public String getPrimaryAlias() throws DatabaseException + public synchronized String getPrimaryAlias() throws DatabaseException { - return null; // TEMP + if (m_prime_alias==null) + m_prime_alias = m_ops.getPrimaryAlias(m_id); + return m_prime_alias; - } + } // end getPrimaryAlias public void setPrimaryAlias(DynamoUser caller, String alias) throws DatabaseException, DynamoSecurityException { - } + testPermission(caller,ConfNamespaces.PERMISSIONS_NAMESPACE,"set.alias","auth.setAlias"); + synchronized (this) + { // change the prime alias + java.util.Date update = m_ops.setPrimaryAlias(m_id,alias); + m_prime_alias = alias; + if (update!=null) + m_lastupdate = update; + + } // end synchronized block + + } // end setPrimaryAlias public void addAlias(DynamoUser caller, String alias) throws DatabaseException, DynamoSecurityException { - } + testPermission(caller,ConfNamespaces.PERMISSIONS_NAMESPACE,"set.alias","auth.setAlias"); + synchronized (this) + { // add an alias + java.util.Date update = m_ops.addAlias(m_id,alias); + if (update!=null) + m_lastupdate = update; + + } // end synchronized block + + } // end addAlias public void removeAlias(DynamoUser caller, String alias) throws DatabaseException, DynamoSecurityException { - } + testPermission(caller,ConfNamespaces.PERMISSIONS_NAMESPACE,"set.alias","auth.setAlias"); + synchronized (this) + { // remove the alias + java.util.Date update = m_ops.removeAlias(m_id,alias); + if (update!=null) + m_lastupdate = update; + + } // end synchronized block + + } // end removeAlias } // end class ConferenceImpl diff --git a/src/conferencing-module/com/silverwrist/venice/conf/impl/ConferenceManager.java b/src/conferencing-module/com/silverwrist/venice/conf/impl/ConferenceManager.java index 988ee2c..20fb7c3 100644 --- a/src/conferencing-module/com/silverwrist/venice/conf/impl/ConferenceManager.java +++ b/src/conferencing-module/com/silverwrist/venice/conf/impl/ConferenceManager.java @@ -20,8 +20,11 @@ package com.silverwrist.venice.conf.impl; import java.security.acl.AclNotFoundException; import java.util.*; import org.apache.commons.collections.*; +import com.silverwrist.dynamo.db.NamespaceCache; +import com.silverwrist.dynamo.db.UserManagement; import com.silverwrist.dynamo.except.*; import com.silverwrist.dynamo.iface.*; +import com.silverwrist.dynamo.security.SecurityReferenceMonitor; import com.silverwrist.venice.iface.*; import com.silverwrist.venice.conf.ConfNamespaces; import com.silverwrist.venice.conf.iface.*; @@ -37,6 +40,9 @@ public class ConferenceManager implements ConferenceAccessObject private DynamicImplConferenceAccess m_dobj; private UseCount m_uc; private ConferenceManagerOps m_ops; + private NamespaceCache m_nscache; + private SecurityReferenceMonitor m_srm; + private UserManagement m_users; // user management object private ReferenceMap m_confs; /*-------------------------------------------------------------------------------- @@ -44,7 +50,8 @@ public class ConferenceManager implements ConferenceAccessObject *-------------------------------------------------------------------------------- */ - public ConferenceManager(UseCount uc, DBConnectionPool pool) throws ModuleException + public ConferenceManager(UseCount uc, DBConnectionPool pool, NamespaceCache nscache, SecurityReferenceMonitor srm, + UserManagement users) throws ModuleException { try { // initialize everything @@ -56,10 +63,14 @@ public class ConferenceManager implements ConferenceAccessObject } // end try catch (ConfigException e) { // convert it to a ModuleException + uc.decrementUseCount(); throw new ModuleException(e); } // end catch + m_nscache = nscache; + m_srm = srm; + m_users = users; m_confs = new ReferenceMap(ReferenceMap.HARD,ReferenceMap.SOFT); } // end constructor @@ -75,7 +86,7 @@ public class ConferenceManager implements ConferenceAccessObject ConferenceImpl rc = (ConferenceImpl)(m_confs.get(key)); if (rc==null) { // create a new conference implementation object and save it - rc = new ConferenceImpl(m_ops.getConferenceOps(),params); + rc = new ConferenceImpl(m_ops.getConferenceOps(),m_nscache,m_srm,m_users,params); m_confs.put(key,rc); } // end if @@ -238,6 +249,9 @@ public class ConferenceManager implements ConferenceAccessObject public void dispose() { + m_nscache = null; + m_srm = null; + m_users = null; m_ops.dispose(); m_ops = null; m_uc.decrementUseCount(); diff --git a/src/conferencing-module/com/silverwrist/venice/conf/impl/ConferenceMessages.properties b/src/conferencing-module/com/silverwrist/venice/conf/impl/ConferenceMessages.properties index bc0619f..01daf9e 100644 --- a/src/conferencing-module/com/silverwrist/venice/conf/impl/ConferenceMessages.properties +++ b/src/conferencing-module/com/silverwrist/venice/conf/impl/ConferenceMessages.properties @@ -20,3 +20,12 @@ confid.notfound=No conference was found with conference ID #{0}. confalias.notfound=No conference was found with conference alias "{0}." confid.comm.notfound=No conference was found with conference ID #{0} in community "{1}." confalias.comm.notfound=No conference was found with conference alias "{0}" in community "{1}." +property.serialize=The value of property "{0}" could not be serialized. +property.deserialize=The value of property "{0}" could not be deserialized. +auth.setProperty=You are not authorized to set properties on the conference "{0}." +auth.removeProperty=You are not authorized to remove properties from the conference "{0}." +auth.setName=You are not authorized to change the name of the conference "{0}." +auth.setAlias=You are not authorized to change the aliases of the conference "{0}." +no.remove.prime=You cannot remove the primary alias from a conference. +auth.sequence=You are not authorized to change the sequencing of conferences. +auth.hide.show=You are not permitted to hide or show conferences in this community. diff --git a/src/conferencing-module/com/silverwrist/venice/conf/impl/ConferenceOps.java b/src/conferencing-module/com/silverwrist/venice/conf/impl/ConferenceOps.java index 7ee5473..dcc6850 100644 --- a/src/conferencing-module/com/silverwrist/venice/conf/impl/ConferenceOps.java +++ b/src/conferencing-module/com/silverwrist/venice/conf/impl/ConferenceOps.java @@ -21,6 +21,7 @@ import java.util.*; import com.silverwrist.dynamo.db.OpsBase; import com.silverwrist.dynamo.except.*; import com.silverwrist.dynamo.iface.*; +import com.silverwrist.dynamo.util.*; abstract class ConferenceOps extends OpsBase { @@ -40,4 +41,28 @@ abstract class ConferenceOps extends OpsBase *-------------------------------------------------------------------------------- */ + abstract Object getProperty(int confid, PropertyKey key) throws DatabaseException; + + abstract DateObjectPair setProperty(int confid, PropertyKey key, Object value) throws DatabaseException; + + abstract DateObjectPair removeProperty(int confid, PropertyKey key) throws DatabaseException; + + abstract int[] getPropertyNamespaceIDs(int confid) throws DatabaseException; + + abstract Map getAllProperties(int confid, int namespace) throws DatabaseException; + + abstract java.util.Date setName(int confid, String name) throws DatabaseException; + + abstract void setLastUpdateDate(int confid, java.util.Date date) throws DatabaseException; + + abstract Set getAliases(int confid) throws DatabaseException; + + abstract String getPrimaryAlias(int confid) throws DatabaseException; + + abstract java.util.Date setPrimaryAlias(int confid, String alias) throws DatabaseException; + + abstract java.util.Date addAlias(int confid, String alias) throws DatabaseException; + + abstract java.util.Date removeAlias(int confid, String alias) throws DatabaseException; + } // end class ConferenceOps diff --git a/src/conferencing-module/com/silverwrist/venice/conf/impl/ConferenceOps_mysql.java b/src/conferencing-module/com/silverwrist/venice/conf/impl/ConferenceOps_mysql.java index 72cd16b..26a8954 100644 --- a/src/conferencing-module/com/silverwrist/venice/conf/impl/ConferenceOps_mysql.java +++ b/src/conferencing-module/com/silverwrist/venice/conf/impl/ConferenceOps_mysql.java @@ -26,6 +26,14 @@ import com.silverwrist.dynamo.util.*; class ConferenceOps_mysql extends ConferenceOps { + /*-------------------------------------------------------------------------------- + * Attributes + *-------------------------------------------------------------------------------- + */ + + private DBUtilities m_utils; // reference to utilities object + private PropertySerializer m_psz; // reference to property serializer + /*-------------------------------------------------------------------------------- * Constructor *-------------------------------------------------------------------------------- @@ -34,12 +42,681 @@ class ConferenceOps_mysql extends ConferenceOps ConferenceOps_mysql(DBConnectionPool pool) { super(pool); + m_utils = (DBUtilities)(pool.queryService(DBUtilities.class)); + m_psz = (PropertySerializer)(pool.queryService(PropertySerializer.class)); } // end constructor + /*-------------------------------------------------------------------------------- + * Internal operations + *-------------------------------------------------------------------------------- + */ + + private final java.util.Date touchConference(Connection conn, int confid, java.util.Date date) throws SQLException + { + PreparedStatement stmt = null; + try + { // prepare and execute the update statement + stmt = conn.prepareStatement("UPDATE conferences SET lastupdate = ? WHERE confid = ?;"); + m_utils.setDateTime(stmt,1,date); + stmt.setInt(2,confid); + stmt.executeUpdate(); + return date; + + } // end try + finally + { // make sure and close out the statement + SQLUtils.shutdown(stmt); + + } // end finally + + } // end touchConference + + private final java.util.Date touchConference(Connection conn, int confid) throws SQLException + { + return this.touchConference(conn,confid,new java.util.Date()); + + } // end touchConference + + /*-------------------------------------------------------------------------------- + * Overrides from class OpsBase + *-------------------------------------------------------------------------------- + */ + + public void dispose() + { + m_psz = null; + m_utils = null; + super.dispose(); + + } // end dispose + /*-------------------------------------------------------------------------------- * Abstract implementations from class ConferenceOps *-------------------------------------------------------------------------------- */ + Object getProperty(int confid, PropertyKey key) throws DatabaseException + { + Connection conn = null; + PreparedStatement stmt = null; + ResultSet rs = null; + String rc_str = null; + try + { // get a connection + conn = getConnection(); + + // look up the property + stmt = conn.prepareStatement("SELECT prop_value FROM conf_props WHERE confid = ? AND nsid = ? " + + "AND prop_name = ?;"); + stmt.setInt(1,confid); + stmt.setInt(2,key.getNamespaceID()); + stmt.setString(3,key.getName()); + rs = stmt.executeQuery(); + if (!(rs.next())) + return null; // property not found + + rc_str = rs.getString(1); + + } // 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 + + // Deserialize the property value. + Object rc = m_psz.deserializeProperty(rc_str); + if (rc!=null) + return rc; + + // deserialization exception - throw it + DatabaseException de = new DatabaseException(ConferenceOps_mysql.class,"ConferenceMessages", + "property.deserialize"); + de.setParameter(0,key.getName()); + throw de; + + } // end getProperty + + DateObjectPair setProperty(int confid, PropertyKey key, Object value) throws DatabaseException + { + String serialized_value = m_psz.serializeProperty(value); + if (serialized_value==null) + { // serialization exception - throw it + DatabaseException de = new DatabaseException(ConferenceOps_mysql.class,"ConferenceMessages", + "property.serialize"); + de.setParameter(0,key.getName()); + throw de; + + } // end if + + String old_value = null; + Connection conn = null; + PreparedStatement stmt = null; + Statement stmt2 = null; + ResultSet rs = null; + java.util.Date update = null; + try + { // get a connection + conn = getConnection(); + + // lock the table + stmt2 = conn.createStatement(); + stmt2.executeUpdate("LOCK TABLES conf_props WRITE, conferences WRITE;"); + + // look to see if the property value is already there + stmt = conn.prepareStatement("SELECT prop_value FROM conf_props WHERE confid = ? AND nsid = ? " + + "AND prop_name = ?;"); + stmt.setInt(1,confid); + stmt.setInt(2,key.getNamespaceID()); + stmt.setString(3,key.getName()); + rs = stmt.executeQuery(); + if (rs.next()) + old_value = rs.getString(1); + SQLUtils.shutdown(rs); + rs = null; + SQLUtils.shutdown(stmt); + + if (old_value!=null) + { // prepare the statement to update the existing record + stmt = conn.prepareStatement("UPDATE conf_props SET prop_value = ? WHERE confid = ? AND nsid = ? " + + "AND prop_name = ?;"); + stmt.setString(1,serialized_value); + stmt.setInt(2,confid); + stmt.setInt(3,key.getNamespaceID()); + stmt.setString(4,key.getName()); + + } // end if + else + { // prepare the statement to insert a new record + stmt = conn.prepareStatement("INSERT INTO conf_props (confid, nsid, prop_name, prop_value) " + + "VALUES (?, ?, ?, ?);"); + stmt.setInt(1,confid); + stmt.setInt(2,key.getNamespaceID()); + stmt.setString(3,key.getName()); + stmt.setString(4,serialized_value); + + } // end else + + stmt.executeUpdate(); // execute it! + + // Touch the conference entry. + update = touchConference(conn,confid); + + } // 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 + + if (old_value==null) + return new DateObjectPair(null,update); // no previous value + + // Deserialize the property value. + Object rc = m_psz.deserializeProperty(old_value); + if (rc!=null) + return new DateObjectPair(rc,update); + + // deserialization exception - throw it + DatabaseException de = new DatabaseException(ConferenceOps_mysql.class,"ConferenceMessages", + "property.deserialize"); + de.setParameter(0,key.getName()); + throw de; + + } // end setProperty + + DateObjectPair removeProperty(int confid, PropertyKey key) throws DatabaseException + { + String old_value = null; + Connection conn = null; + PreparedStatement stmt = null; + Statement stmt2 = null; + ResultSet rs = null; + java.util.Date update = null; + try + { // get a connection + conn = getConnection(); + + // lock the table + stmt2 = conn.createStatement(); + stmt2.executeUpdate("LOCK TABLES conf_props WRITE, conferences WRITE;"); + + // look to see if the property value is already there + stmt = conn.prepareStatement("SELECT prop_value FROM conf_props WHERE confid = ? AND nsid = ? " + + "AND prop_name = ?;"); + stmt.setInt(1,confid); + stmt.setInt(2,key.getNamespaceID()); + stmt.setString(3,key.getName()); + rs = stmt.executeQuery(); + if (rs.next()) + old_value = rs.getString(1); + else + return null; // no need to remove anything + + SQLUtils.shutdown(rs); + rs = null; + SQLUtils.shutdown(stmt); + + // delete the database row + stmt = conn.prepareStatement("DELETE FROM conf_props WHERE confid = ? AND nsid = ? AND prop_name = ?;"); + stmt.setInt(1,confid); + stmt.setInt(2,key.getNamespaceID()); + stmt.setString(3,key.getName()); + stmt.executeUpdate(); + + // Touch the conference entry. + update = touchConference(conn,confid); + + } // 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 + + // Deserialize the property value. + Object rc = m_psz.deserializeProperty(old_value); + if (rc!=null) + return new DateObjectPair(rc,update); + + // deserialization exception - throw it + DatabaseException de = new DatabaseException(ConferenceOps_mysql.class,"ConferenceMessages", + "property.deserialize"); + de.setParameter(0,key.getName()); + throw de; + + } // end removeProperty + + int[] getPropertyNamespaceIDs(int confid) throws DatabaseException + { + Connection conn = null; + PreparedStatement stmt = null; + ResultSet rs = null; + try + { // get a connection + conn = getConnection(); + + // execute the query! + stmt = conn.prepareStatement("SELECT DISTINCT nsid FROM conf_props WHERE confid = ?;"); + stmt.setInt(1,confid); + rs = stmt.executeQuery(); + + // read out a list of the namespace IDs + ArrayList tmp = new ArrayList(); + while (rs.next()) + tmp.add(new Integer(rs.getInt(1))); + + // create and return the array + int[] rc = new int[tmp.size()]; + for (int i=0; i