[Red5commits] [2444] use attribute store to keep SO properties internally - this greatly improves SO

jbauch luke at codegent.com
Tue Oct 30 23:51:03 PDT 2007


use attribute store to keep SO properties internally - this greatly improves SO performance


Timestamp: 10/25/07 18:01:32 EST (5 days ago) 
Change: 2444 
Author: jbauch

Files (see diff or trac for details): 
java/server/trunk/src/org/red5/server/so/ClientSharedObject.java
java/server/trunk/src/org/red5/server/so/SharedObject.java
java/server/trunk/src/org/red5/server/so/SharedObjectScope.java


Trac: http://mirror1.cvsdude.com/trac/osflash/red5/changeset/2444

Index: /java/server/trunk/src/org/red5/server/so/SharedObjectScope.java
===================================================================
--- /java/server/trunk/src/org/red5/server/so/SharedObjectScope.java (revision 2397)
+++ /java/server/trunk/src/org/red5/server/so/SharedObjectScope.java (revision 2444)
@@ -340,4 +340,10 @@
 	/** {@inheritDoc} */
     @Override
+	public Object getAttribute(String name, Object value) {
+		return so.getAttribute(name, value);
+	}
+
+	/** {@inheritDoc} */
+    @Override
 	public Map<String, Object> getAttributes() {
 		return so.getAttributes();
@@ -463,5 +469,5 @@
      * @return
      */
-    protected boolean isSendAllowed(String message, List arguments) {
+    protected boolean isSendAllowed(String message, List<?> arguments) {
     	// Check internal handlers first
     	for (ISharedObjectSecurity handler: securityHandlers) {
@@ -549,5 +555,5 @@
 					case SERVER_SEND_MESSAGE:
 						final String message = event.getKey();
-						final List arguments = (List) event.getValue();
+						final List<?> arguments = (List<?>) event.getValue();
 						// Ignore request silently if not allowed
 						if (isSendAllowed(message, arguments)) {
@@ -696,5 +702,18 @@
 	/** {@inheritDoc} */
     public boolean clear() {
-		return so.clear();
+    	boolean success;
+    	beginUpdate();
+    	try {
+    		success = so.clear();
+    	} finally {
+    		endUpdate();
+    	}
+    	
+		if (success) {
+			for (ISharedObjectListener listener : serverListeners) {
+				listener.onSharedObjectClear(this);
+			}
+        }
+		return success;
 	}
 
Index: /java/server/trunk/src/org/red5/server/so/ClientSharedObject.java
===================================================================
--- /java/server/trunk/src/org/red5/server/so/ClientSharedObject.java (revision 2397)
+++ /java/server/trunk/src/org/red5/server/so/ClientSharedObject.java (revision 2444)
@@ -146,5 +146,5 @@
 						
 					case CLIENT_CLEAR_DATA:
-						data.clear();
+						attributes.clear();
 						notifyClear();
 						break;
@@ -152,19 +152,19 @@
 					case CLIENT_DELETE_DATA:
 					case CLIENT_DELETE_ATTRIBUTE:
-						data.remove(event.getKey());
+						attributes.remove(event.getKey());
 						notifyDelete(event.getKey());
 						break;
 						
 					case CLIENT_SEND_MESSAGE:
-						notifySendMessage(event.getKey(), (List) event.getValue());
+						notifySendMessage(event.getKey(), (List<?>) event.getValue());
 						break;
 						
 					case CLIENT_UPDATE_DATA:
-						data.putAll((Map<String, Object>) event.getValue());
+						attributes.putAll((Map<String, Object>) event.getValue());
 						notifyUpdate(event.getKey(), (Map<String, Object>) event.getValue());
 						break;
 						
 					case CLIENT_UPDATE_ATTRIBUTE:
-						data.put(event.getKey(), event.getValue());
+						attributes.put(event.getKey(), event.getValue());
 						notifyUpdate(event.getKey(), event.getValue());
 						break;
@@ -248,5 +248,5 @@
      * @param params        Params
      */
-    protected void notifySendMessage(String method, List params) {
+    protected void notifySendMessage(String method, List<?> params) {
         for (ISharedObjectListener listener : listeners) {
             listener.onSharedObjectSend(this, method, params);
@@ -257,5 +257,9 @@
     @Override
 	public synchronized boolean setAttribute(String name, Object value) {
-		ownerMessage.addEvent(Type.SERVER_SET_ATTRIBUTE, name, null);
+    	if (value == null) {
+    		return removeAttribute(name);
+    	}
+    	
+		ownerMessage.addEvent(Type.SERVER_SET_ATTRIBUTE, name, value);
 		notifyModified();
 		return true;
@@ -265,5 +269,5 @@
     @Override
 	public synchronized void setAttributes(IAttributeStore values) {
-		super.setAttributes(values);
+    	setAttributes(values.getAttributes());
 	}
 
@@ -271,5 +275,7 @@
     @Override
 	public synchronized void setAttributes(Map<String, Object> values) {
-		super.setAttributes(values);
+		for (Map.Entry<String, Object> entry : values.entrySet()) {
+			setAttribute(entry.getKey(), entry.getValue());
+		}
 	}
 
@@ -294,5 +300,5 @@
 		// TODO: there must be a direct way to clear the SO on the client
 		// side...
-        for (String key : data.keySet()) {
+        for (String key : getAttributeNames()) {
             ownerMessage.addEvent(Type.SERVER_DELETE_ATTRIBUTE, key, null);
         }
@@ -380,54 +386,4 @@
 
 	/** {@inheritDoc} */
-    public Boolean getBoolAttribute(String name) {
-		return (Boolean) getAttribute(name);
-	}
-
-	/** {@inheritDoc} */
-    public Byte getByteAttribute(String name) {
-		return (Byte) getAttribute(name);
-	}
-
-	/** {@inheritDoc} */
-    public Double getDoubleAttribute(String name) {
-		return (Double) getAttribute(name);
-	}
-
-	/** {@inheritDoc} */
-    public Integer getIntAttribute(String name) {
-		return (Integer) getAttribute(name);
-	}
-
-	/** {@inheritDoc} */
-    public List getListAttribute(String name) {
-		return (List) getAttribute(name);
-	}
-
-	/** {@inheritDoc} */
-    public Long getLongAttribute(String name) {
-		return (Long) getAttribute(name);
-	}
-
-	/** {@inheritDoc} */
-    public Map getMapAttribute(String name) {
-		return (Map) getAttribute(name);
-	}
-
-	/** {@inheritDoc} */
-    public Set getSetAttribute(String name) {
-		return (Set) getAttribute(name);
-	}
-
-	/** {@inheritDoc} */
-    public Short getShortAttribute(String name) {
-		return (Short) getAttribute(name);
-	}
-
-	/** {@inheritDoc} */
-    public String getStringAttribute(String name) {
-		return (String) getAttribute(name);
-	}
-
-	/** {@inheritDoc} */
     public synchronized Object getAttribute(String name, Object defaultValue) {
 		if (!hasAttribute(name)) {
Index: /java/server/trunk/src/org/red5/server/so/SharedObject.java
===================================================================
--- /java/server/trunk/src/org/red5/server/so/SharedObject.java (revision 2397)
+++ /java/server/trunk/src/org/red5/server/so/SharedObject.java (revision 2444)
@@ -23,11 +23,8 @@
 
 import java.io.IOException;
-import java.util.Collections;
-import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.CopyOnWriteArraySet;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -37,4 +34,5 @@
 import org.red5.io.object.Output;
 import org.red5.io.object.Serializer;
+import org.red5.server.AttributeStore;
 import org.red5.server.api.IAttributeStore;
 import org.red5.server.api.event.IEventListener;
@@ -66,8 +64,8 @@
  * SOs store data as simple map, that is, "name-value" pairs. Each value in turn can be complex object or map.
  * 
- * All access to protected methods that change properties in the SO must be properly
+ * All access to methods that change properties in the SO must be properly
  * synchronized for multi-threaded access.
  */
-public class SharedObject implements ISharedObjectStatistics, IPersistable, Constants {
+public class SharedObject extends AttributeStore implements ISharedObjectStatistics, IPersistable, Constants {
     /**
      * Logger
@@ -106,14 +104,4 @@
      */
     protected int version = 1;
-
-    /**
-     * SO data
-     */
-    protected Map<String, Object> data;
-
-    /**
-     * SO hashes
-     */
-    protected Map<String, Integer> hashes = new HashMap<String, Integer>();
 
     /**
@@ -185,6 +173,5 @@
     public SharedObject() {
 		// This is used by the persistence framework
-        data = null;
-        data = new ConcurrentHashMap<String, Object>();
+        super();
 
 		ownerMessage = new SharedObjectMessage(null, null, -1, false);
@@ -215,7 +202,5 @@
     public SharedObject(Map<String, Object> data, String name, String path,
 			boolean persistent) {
-        this.data = null;
-        this.data = new ConcurrentHashMap<String, Object>();
-		this.data.putAll(data);
+    	super();
 		this.name = name;
 		this.path = path;
@@ -225,4 +210,5 @@
 		ownerMessage = new SharedObjectMessage(null, name, 0, persistent);
         creationTime = System.currentTimeMillis();
+        super.setAttributes(data);
 	}
 
@@ -237,15 +223,6 @@
     public SharedObject(Map<String, Object> data, String name, String path,
 			boolean persistent, IPersistenceStore storage) {
-        this.data = null;
-        this.data = new ConcurrentHashMap<String, Object>();
-		this.data.putAll(data);
-		this.name = name;
-		this.path = path;
-        persistentSO = false;
-        this.persistentSO = persistent;
+    	this(data, name, path, persistent);
 		setStore(storage);
-
-		ownerMessage = new SharedObjectMessage(null, name, 0, persistent);
-        creationTime = System.currentTimeMillis();
 	}
 
@@ -360,15 +337,4 @@
 
     /**
-     * Update hashes
-     */
-    private void updateHashes() {
-		hashes.clear();
-		for (String name : data.keySet()) {
-			Object value = data.get(name);
-			hashes.put(name, value != null ? value.hashCode() : 0);
-		}
-	}
-
-    /**
      * Send notification about modification of SO
      */
@@ -392,5 +358,4 @@
 
 		sendUpdates();
-		updateHashes();
 	}
 
@@ -414,36 +379,27 @@
     
     /**
-     * Check whether this SO has given attribute
-     * @param name          Attribute name
-     * @return              <code>true</code> if this SO has attribute with given name, <code>false</code> otherwise
-     */
-    public boolean hasAttribute(String name) {
-		return data.containsKey(name);
-	}
-
-	/**
-     * Return attribute names as set.
-     *
-     * @return  Set of attribute names
-     */
-    public Set<String> getAttributeNames() {
-		return Collections.unmodifiableSet(data.keySet());
-	}
-
-    /**
-     * Return map of attributes of this SO
-     * @return   Map of attributes
-     */
-    public Map<String, Object> getAttributes() {
-		return Collections.unmodifiableMap(data);
-	}
-
-    /**
-     * Return attribute by name
+     * Return attribute by name and set if it doesn't exist yet.
      * @param name         Attribute name
+     * @param value        Value to set if attribute doesn't exist
      * @return             Attribute value
      */
-    public Object getAttribute(String name) {
-		return data.get(name);
+    @Override
+    public Object getAttribute(String name, Object value) {
+    	if (name == null) {
+    		return null;
+    	}
+    	
+    	Object result = attributes.putIfAbsent(name, value);
+    	if (result == null) {
+    		// No previous value
+			modified = true;
+    		ownerMessage.addEvent(Type.CLIENT_UPDATE_DATA, name, value);
+    		syncEvents.add(new SharedObjectEvent(Type.CLIENT_UPDATE_DATA, name, value));
+    		notifyModified();
+			changeStats.incrementAndGet();
+    		result = value;
+    	}
+    	
+		return result;
 	}
 
@@ -454,13 +410,18 @@
      * @return             <code>true</code> if there's such attribute and value was set, <code>false</code> otherwise
      */
-    protected boolean setAttribute(String name, Object value) {
+    @Override
+    public boolean setAttribute(String name, Object value) {
 		ownerMessage.addEvent(Type.CLIENT_UPDATE_ATTRIBUTE, name, null);
-		Object old = data.get(name);
-		Integer oldHash = (value != null ? value.hashCode() : 0);
-		if (old == null || !old.equals(value)
-				|| !oldHash.equals(hashes.get(name))) {
+		if (value == null && super.removeAttribute(name)) {
+			// Setting a null value removes the attribute
 			modified = true;
-			data.put(name, value);
+			syncEvents.add(new SharedObjectEvent(Type.CLIENT_DELETE_DATA, name,
+					null));
+			deleteStats.incrementAndGet();
+			notifyModified();
+			return true;
+		} else if (value != null && super.setAttribute(name, value)) {
 			// only sync if the attribute changed
+			modified = true;
 			syncEvents.add(new SharedObjectEvent(Type.CLIENT_UPDATE_DATA, name,
 					value));
@@ -479,5 +440,6 @@
      * @param values  Attributes.
      */
-    protected void setAttributes(Map<String, Object> values) {
+    @Override
+    public void setAttributes(Map<String, Object> values) {
 		if (values == null) {
 			return;
@@ -485,8 +447,11 @@
 
 		beginUpdate();
-        for (String name : values.keySet()) {
-            setAttribute(name, values.get(name));
-        }
-        endUpdate();
+		try {
+			for (Map.Entry<String, Object> entry : values.entrySet()) {
+				setAttribute(entry.getKey(), entry.getValue());
+			}
+		} finally {
+			endUpdate();
+		}
 	}
 
@@ -496,14 +461,11 @@
      * @param values  Attributes.
      */
-    protected void setAttributes(IAttributeStore values) {
+    @Override
+    public void setAttributes(IAttributeStore values) {
 		if (values == null) {
 			return;
 		}
 
-		beginUpdate();
-        for (String name : values.getAttributeNames()) {
-            setAttribute(name, values.getAttribute(name));
-        }
-        endUpdate();
+		setAttributes(values.getAttributes());
 	}
 
@@ -513,19 +475,19 @@
      * @return        <code>true</code> if there's such an attribute and it was removed, <code>false</code> otherwise
      */
-    protected boolean removeAttribute(String name) {
-		boolean result = data.containsKey(name);
-		if (result) {
-			data.remove(name);
-		}
+    @Override
+    public boolean removeAttribute(String name) {
 		// Send confirmation to client
 		ownerMessage.addEvent(Type.CLIENT_DELETE_DATA, name, null);
-		if (result) {
+    	if (super.removeAttribute(name)) {
 			modified = true;
 			syncEvents.add(new SharedObjectEvent(Type.CLIENT_DELETE_DATA, name,
 					null));
 			deleteStats.incrementAndGet();
-		}
-		notifyModified();
-		return result;
+			notifyModified();
+			return true;
+		} else {
+			notifyModified();
+			return false;
+		}
 	}
 
@@ -535,5 +497,5 @@
      * @param arguments       Arguments
      */
-    protected void sendMessage(String handler, List arguments) {
+    protected void sendMessage(String handler, List<?> arguments) {
         // Forward
         ownerMessage.addEvent(Type.CLIENT_SEND_MESSAGE, handler, arguments);
@@ -549,5 +511,5 @@
      */
     public Map<String, Object> getData() {
-		return Collections.unmodifiableMap(data);
+		return getAttributes();
 	}
 
@@ -571,14 +533,16 @@
      * Remove all attributes (clear Shared Object)
      */
-	protected void removeAttributes() {
+	@Override
+	public void removeAttributes() {
 		// TODO: there must be a direct way to clear the SO on the client side...
-        for (String key : data.keySet()) {
+		Set<String> names = getAttributeNames();
+        for (String key : names) {
             ownerMessage.addEvent(Type.CLIENT_DELETE_DATA, key, null);
             syncEvents.add(new SharedObjectEvent(Type.CLIENT_DELETE_DATA, key,
                     null));
         }
-		deleteStats.addAndGet(data.size());
+		deleteStats.addAndGet(names.size());
         // Clear data
-		data.clear();
+		super.removeAttributes();
         // Mark as modified
         modified = true;
@@ -600,7 +564,7 @@
 			ownerMessage.addEvent(Type.CLIENT_CLEAR_DATA, null, null);
 		}
-		if (!data.isEmpty()) {
+		if (!attributes.isEmpty()) {
 			ownerMessage.addEvent(new SharedObjectEvent(
-					Type.CLIENT_UPDATE_DATA, null, getData()));
+					Type.CLIENT_UPDATE_DATA, null, getAttributes()));
 		}
 
@@ -682,15 +646,14 @@
 		Serializer ser = new Serializer();
 		ser.serialize(output, getName());
-		ser.serialize(output, data);
-	}
-
-	/** {@inheritDoc} */
+		ser.serialize(output, getAttributes());
+	}
+
+	/** {@inheritDoc} */
+    @SuppressWarnings("unchecked")
     public void deserialize(Input input) throws IOException {
 		Deserializer deserializer = new Deserializer();
 		name = (String) deserializer.deserialize(input);
 		persistentSO = persistent = true;
-		data.clear();
-		data.putAll((Map<String, Object>) deserializer.deserialize(input));
-		updateHashes();
+		super.setAttributes((Map<String, Object>) deserializer.deserialize(input));
 		ownerMessage.setName(name);
 		ownerMessage.setIsPersistent(true);
@@ -714,9 +677,11 @@
 	 */
     protected boolean clear() {
-		data.clear();
+    	super.removeAttributes();
 		// Send confirmation to client
 		ownerMessage.addEvent(Type.CLIENT_CLEAR_DATA, name, null);
+		notifyModified();
+		changeStats.incrementAndGet();
         // Is it clear now?
-        return data.isEmpty();
+        return true;
 	}
 
@@ -728,7 +693,6 @@
     protected void close() {
 		// clear collections
-		data.clear();
+		super.removeAttributes();
 		listeners.clear();
-		hashes.clear();
 		syncEvents.clear();
 		ownerMessage.getEvents().clear();


Note:
Diffs are chopped if more than 25k.
This is to get past the limit on the mailing list.



More information about the Red5commits mailing list