[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