Commit 9c2a7955 authored by guus's avatar guus

OF-497: Properly determine the size of Collections to be cached. Patch by Marcin Cieślak.

git-svn-id: http://svn.igniterealtime.org/svn/repos/openfire/trunk@12942 b35dd754-fafc-0310-a699-88a17e54d16e
parent 03192ac7
...@@ -22,6 +22,7 @@ package org.jivesoftware.openfire.entitycaps; ...@@ -22,6 +22,7 @@ package org.jivesoftware.openfire.entitycaps;
import org.jivesoftware.util.cache.CacheSizes; import org.jivesoftware.util.cache.CacheSizes;
import org.jivesoftware.util.cache.Cacheable; import org.jivesoftware.util.cache.Cacheable;
import org.jivesoftware.util.cache.CannotCalculateSizeException;
import org.jivesoftware.util.cache.ExternalizableUtil; import org.jivesoftware.util.cache.ExternalizableUtil;
import java.io.Externalizable; import java.io.Externalizable;
...@@ -136,7 +137,7 @@ public class EntityCapabilities implements Cacheable, Externalizable { ...@@ -136,7 +137,7 @@ public class EntityCapabilities implements Cacheable, Externalizable {
ExternalizableUtil.getInstance().writeSafeUTF(out, verAttribute); ExternalizableUtil.getInstance().writeSafeUTF(out, verAttribute);
} }
public int getCachedSize() { public int getCachedSize() throws CannotCalculateSizeException {
int size = CacheSizes.sizeOfCollection(identities); int size = CacheSizes.sizeOfCollection(identities);
size += CacheSizes.sizeOfCollection(features); size += CacheSizes.sizeOfCollection(features);
size += CacheSizes.sizeOfString(verAttribute); size += CacheSizes.sizeOfString(verAttribute);
......
...@@ -44,6 +44,7 @@ import org.jivesoftware.openfire.XMPPServer; ...@@ -44,6 +44,7 @@ import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.event.GroupEventDispatcher; import org.jivesoftware.openfire.event.GroupEventDispatcher;
import org.jivesoftware.util.cache.CacheSizes; import org.jivesoftware.util.cache.CacheSizes;
import org.jivesoftware.util.cache.Cacheable; import org.jivesoftware.util.cache.Cacheable;
import org.jivesoftware.util.cache.CannotCalculateSizeException;
import org.jivesoftware.util.cache.ExternalizableUtil; import org.jivesoftware.util.cache.ExternalizableUtil;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
...@@ -347,7 +348,8 @@ public class Group implements Cacheable, Externalizable { ...@@ -347,7 +348,8 @@ public class Group implements Cacheable, Externalizable {
} }
} }
public int getCachedSize() { public int getCachedSize()
throws CannotCalculateSizeException {
// Approximate the size of the object in bytes by calculating the size // Approximate the size of the object in bytes by calculating the size
// of each field. // of each field.
int size = 0; int size = 0;
...@@ -717,4 +719,4 @@ public class Group implements Cacheable, Externalizable { ...@@ -717,4 +719,4 @@ public class Group implements Cacheable, Externalizable {
ExternalizableUtil.getInstance().readSerializableCollection(in, members, getClass().getClassLoader()); ExternalizableUtil.getInstance().readSerializableCollection(in, members, getClass().getClassLoader());
ExternalizableUtil.getInstance().readSerializableCollection(in, administrators, getClass().getClassLoader()); ExternalizableUtil.getInstance().readSerializableCollection(in, administrators, getClass().getClassLoader());
} }
} }
\ No newline at end of file
...@@ -38,6 +38,7 @@ import org.jivesoftware.openfire.roster.Roster; ...@@ -38,6 +38,7 @@ import org.jivesoftware.openfire.roster.Roster;
import org.jivesoftware.openfire.user.UserNotFoundException; import org.jivesoftware.openfire.user.UserNotFoundException;
import org.jivesoftware.util.cache.CacheSizes; import org.jivesoftware.util.cache.CacheSizes;
import org.jivesoftware.util.cache.Cacheable; import org.jivesoftware.util.cache.Cacheable;
import org.jivesoftware.util.cache.CannotCalculateSizeException;
import org.jivesoftware.util.cache.ExternalizableUtil; import org.jivesoftware.util.cache.ExternalizableUtil;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
...@@ -247,7 +248,7 @@ public class PrivacyList implements Cacheable, Externalizable { ...@@ -247,7 +248,7 @@ public class PrivacyList implements Cacheable, Externalizable {
return null; return null;
} }
public int getCachedSize() { public int getCachedSize() throws CannotCalculateSizeException {
// Approximate the size of the object in bytes by calculating the size // Approximate the size of the object in bytes by calculating the size
// of each field. // of each field.
int size = 0; int size = 0;
......
...@@ -51,6 +51,7 @@ import org.jivesoftware.openfire.user.UserNotFoundException; ...@@ -51,6 +51,7 @@ import org.jivesoftware.openfire.user.UserNotFoundException;
import org.jivesoftware.util.JiveConstants; import org.jivesoftware.util.JiveConstants;
import org.jivesoftware.util.cache.CacheSizes; import org.jivesoftware.util.cache.CacheSizes;
import org.jivesoftware.util.cache.Cacheable; import org.jivesoftware.util.cache.Cacheable;
import org.jivesoftware.util.cache.CannotCalculateSizeException;
import org.jivesoftware.util.cache.ExternalizableUtil; import org.jivesoftware.util.cache.ExternalizableUtil;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
...@@ -732,7 +733,7 @@ public class Roster implements Cacheable, Externalizable { ...@@ -732,7 +733,7 @@ public class Roster implements Cacheable, Externalizable {
} }
} }
public int getCachedSize() { public int getCachedSize() throws CannotCalculateSizeException {
// Approximate the size of the object in bytes by calculating the size // Approximate the size of the object in bytes by calculating the size
// of the content of each field, if that content is likely to be eligable for // of the content of each field, if that content is likely to be eligable for
// garbage collection if the Roster instance is dereferenced. // garbage collection if the Roster instance is dereferenced.
...@@ -743,8 +744,8 @@ public class Roster implements Cacheable, Externalizable { ...@@ -743,8 +744,8 @@ public class Roster implements Cacheable, Externalizable {
// implicitFrom // implicitFrom
for(Map.Entry<String, Set<String>> entry : implicitFrom.entrySet()) { for(Map.Entry<String, Set<String>> entry : implicitFrom.entrySet()) {
size += CacheSizes.sizeOfString(entry.getKey()); size += CacheSizes.sizeOfString(entry.getKey());
size += CacheSizes.sizeOfCollection(entry.getValue()); size += CacheSizes.sizeOfCollection(entry.getValue());
} }
return size; return size;
......
...@@ -29,6 +29,7 @@ import org.jivesoftware.openfire.user.UserNotFoundException; ...@@ -29,6 +29,7 @@ import org.jivesoftware.openfire.user.UserNotFoundException;
import org.jivesoftware.util.IntEnum; import org.jivesoftware.util.IntEnum;
import org.jivesoftware.util.cache.CacheSizes; import org.jivesoftware.util.cache.CacheSizes;
import org.jivesoftware.util.cache.Cacheable; import org.jivesoftware.util.cache.Cacheable;
import org.jivesoftware.util.cache.CannotCalculateSizeException;
import org.jivesoftware.util.cache.ExternalizableUtil; import org.jivesoftware.util.cache.ExternalizableUtil;
import org.xmpp.packet.JID; import org.xmpp.packet.JID;
...@@ -540,7 +541,7 @@ public class RosterItem implements Cacheable, Externalizable { ...@@ -540,7 +541,7 @@ public class RosterItem implements Cacheable, Externalizable {
* *
* @see org.jivesoftware.util.cache.Cacheable#getCachedSize() * @see org.jivesoftware.util.cache.Cacheable#getCachedSize()
*/ */
public int getCachedSize() { public int getCachedSize() throws CannotCalculateSizeException {
int size = jid.toBareJID().length(); int size = jid.toBareJID().length();
size += CacheSizes.sizeOfString(nickname); size += CacheSizes.sizeOfString(nickname);
size += CacheSizes.sizeOfCollection(groups); size += CacheSizes.sizeOfCollection(groups);
...@@ -582,4 +583,4 @@ public class RosterItem implements Cacheable, Externalizable { ...@@ -582,4 +583,4 @@ public class RosterItem implements Cacheable, Externalizable {
askStatus = AskType.getTypeFromInt(ExternalizableUtil.getInstance().readInt(in)); askStatus = AskType.getTypeFromInt(ExternalizableUtil.getInstance().readInt(in));
rosterID = ExternalizableUtil.getInstance().readLong(in); rosterID = ExternalizableUtil.getInstance().readLong(in);
} }
} }
\ No newline at end of file
...@@ -45,6 +45,7 @@ import org.jivesoftware.openfire.roster.Roster; ...@@ -45,6 +45,7 @@ import org.jivesoftware.openfire.roster.Roster;
import org.jivesoftware.util.StringUtils; import org.jivesoftware.util.StringUtils;
import org.jivesoftware.util.cache.CacheSizes; import org.jivesoftware.util.cache.CacheSizes;
import org.jivesoftware.util.cache.Cacheable; import org.jivesoftware.util.cache.Cacheable;
import org.jivesoftware.util.cache.CannotCalculateSizeException;
import org.jivesoftware.util.cache.ExternalizableUtil; import org.jivesoftware.util.cache.ExternalizableUtil;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
...@@ -387,7 +388,8 @@ public class User implements Cacheable, Externalizable, Result { ...@@ -387,7 +388,8 @@ public class User implements Cacheable, Externalizable, Result {
} }
} }
public int getCachedSize() { public int getCachedSize()
throws CannotCalculateSizeException {
// Approximate the size of the object in bytes by calculating the size // Approximate the size of the object in bytes by calculating the size
// of each field. // of each field.
int size = 0; int size = 0;
...@@ -614,4 +616,4 @@ public class User implements Cacheable, Externalizable, Result { ...@@ -614,4 +616,4 @@ public class User implements Cacheable, Externalizable, Result {
{ {
return username; return username;
} }
} }
\ No newline at end of file
...@@ -22,8 +22,12 @@ package org.jivesoftware.util.cache; ...@@ -22,8 +22,12 @@ package org.jivesoftware.util.cache;
import org.jivesoftware.util.cache.Cacheable; import org.jivesoftware.util.cache.Cacheable;
import java.util.Map; import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.util.Collection; import java.util.Collection;
import java.util.Map;
import java.util.Set;
/** /**
* Utility class for determining the sizes in bytes of commonly used objects. * Utility class for determining the sizes in bytes of commonly used objects.
...@@ -112,23 +116,24 @@ public class CacheSizes { ...@@ -112,23 +116,24 @@ public class CacheSizes {
} }
/** /**
* Returns the size in bytes of a Map object. All keys and * Returns the size in bytes of a Map object.
* values <b>must be Strings</b>.
* *
* @param map the Map object to determine the size of. * @param map the Map object to determine the size of.
* @return the size of the Map object. * @return the size of the Map object.
*/ */
public static int sizeOfMap(Map<String, String> map) { public static int sizeOfMap(Map map)
throws CannotCalculateSizeException {
if (map == null) { if (map == null) {
return 0; return 0;
} }
// Base map object -- should be something around this size. // Base map object -- should be something around this size.
int size = 36; int size = 36;
Set<? extends Map.Entry> set = map.entrySet();
// Add in size of each value // Add in size of each value
for (Map.Entry<String, String> entry : map.entrySet()) { for (Map.Entry<Object, Object> entry : set) {
size += sizeOfString(entry.getKey()); size += sizeOfAnything(entry.getKey());
size += sizeOfString(entry.getValue()); size += sizeOfAnything(entry.getValue());
} }
return size; return size;
} }
...@@ -140,7 +145,8 @@ public class CacheSizes { ...@@ -140,7 +145,8 @@ public class CacheSizes {
* @param list the Collection object to determine the size of. * @param list the Collection object to determine the size of.
* @return the size of the Collection object. * @return the size of the Collection object.
*/ */
public static int sizeOfCollection(Collection list) { public static int sizeOfCollection(Collection list)
throws CannotCalculateSizeException {
if (list == null) { if (list == null) {
return 0; return 0;
} }
...@@ -149,17 +155,99 @@ public class CacheSizes { ...@@ -149,17 +155,99 @@ public class CacheSizes {
// Add in size of each value // Add in size of each value
Object[] values = list.toArray(); Object[] values = list.toArray();
for (int i = 0; i < values.length; i++) { for (int i = 0; i < values.length; i++) {
Object obj = values[i]; size += sizeOfAnything(values[i]);
if (obj instanceof String) { }
size += sizeOfString((String)obj); return size;
} }
else if (obj instanceof Long) {
size += sizeOfLong() + sizeOfObject(); /**
* Returns the size of an object in bytes. Determining size by serialization
* is only used as a last resort.
*
* @return the size of an object in bytes.
*/
public static int sizeOfAnything(Object object)
throws CannotCalculateSizeException {
// If the object is Cacheable, ask it its size.
if (object == null) {
return 0;
}
if (object instanceof Cacheable) {
return ((Cacheable)object).getCachedSize();
}
// Check for other common types of objects put into cache.
else if (object instanceof String) {
return sizeOfString((String)object);
}
else if (object instanceof Long) {
return sizeOfLong();
}
else if (object instanceof Integer) {
return sizeOfObject() + sizeOfInt();
}
else if (object instanceof Double) {
return sizeOfObject() + sizeOfDouble();
}
else if (object instanceof Boolean) {
return sizeOfObject() + sizeOfBoolean();
}
else if (object instanceof Map) {
return sizeOfMap((Map)object);
}
else if (object instanceof long[]) {
long[] array = (long[])object;
return sizeOfObject() + array.length * sizeOfLong();
}
else if (object instanceof Collection) {
return sizeOfCollection((Collection)object);
}
else if (object instanceof byte[]) {
byte [] array = (byte[])object;
return sizeOfObject() + array.length;
}
// Default behavior -- serialize the object to determine its size.
else {
int size = 1;
try {
// Default to serializing the object out to determine size.
CacheSizes.NullOutputStream out = new NullOutputStream();
ObjectOutputStream outObj = new ObjectOutputStream(out);
outObj.writeObject(object);
size = out.size();
} }
else { catch (IOException ioe) {
size += ((Cacheable)obj).getCachedSize(); throw new CannotCalculateSizeException(object);
} }
return size;
}
}
private static class NullOutputStream extends OutputStream {
int size = 0;
@Override
public void write(int b) throws IOException {
size++;
}
@Override
public void write(byte[] b) throws IOException {
size += b.length;
}
@Override
public void write(byte[] b, int off, int len) {
size += len;
}
/**
* Returns the number of bytes written out through the stream.
*
* @return the number of bytes written to the stream.
*/
public int size() {
return size;
} }
return size;
} }
} }
\ No newline at end of file
...@@ -42,5 +42,5 @@ public interface Cacheable extends java.io.Serializable { ...@@ -42,5 +42,5 @@ public interface Cacheable extends java.io.Serializable {
* *
* @return the size of the Object in bytes. * @return the size of the Object in bytes.
*/ */
public int getCachedSize(); public int getCachedSize() throws CannotCalculateSizeException;
} }
/**
* $RCSfile$
* $Revision: 11291 $
* $Date: 2009-09-30 10:17:14 +0000 (śro) $
*
* Copyright (C) 2004-2008 Jive Software. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.util.cache;
/**
* <p>Flags an exception when we cannot determine size of the object to be cached.</p>
*
* @author Marcin Cieslak
*/
public class CannotCalculateSizeException extends Exception {
public static final long serialVersionUID = 1754096832201752388L;
public CannotCalculateSizeException() {
}
public CannotCalculateSizeException(Object obj) {
super("Unable to determine size of " + obj.getClass() + " instance");
}
}
...@@ -19,9 +19,6 @@ ...@@ -19,9 +19,6 @@
*/ */
package org.jivesoftware.util.cache; package org.jivesoftware.util.cache;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
...@@ -138,7 +135,13 @@ public class DefaultCache<K, V> implements Cache<K, V> { ...@@ -138,7 +135,13 @@ public class DefaultCache<K, V> implements Cache<K, V> {
// Delete an old entry if it exists. // Delete an old entry if it exists.
V answer = remove(key); V answer = remove(key);
int objectSize = calculateSize(value); int objectSize = 1;
try {
objectSize = CacheSizes.sizeOfAnything(value);
}
catch (CannotCalculateSizeException e) {
Log.warn(e.getMessage(), e);
}
// If the object is bigger than the entire cache, simply don't add it. // If the object is bigger than the entire cache, simply don't add it.
if (maxCacheSize > 0 && objectSize > maxCacheSize * .90) { if (maxCacheSize > 0 && objectSize > maxCacheSize * .90) {
...@@ -541,55 +544,6 @@ public class DefaultCache<K, V> implements Cache<K, V> { ...@@ -541,55 +544,6 @@ public class DefaultCache<K, V> implements Cache<K, V> {
CacheFactory.setMaxLifetimeProperty(name, maxLifetime); CacheFactory.setMaxLifetimeProperty(name, maxLifetime);
} }
/**
* Returns the size of an object in bytes. Determining size by serialization
* is only used as a last resort.
*
* @return the size of an object in bytes.
*/
private int calculateSize(Object object) {
// If the object is Cacheable, ask it its size.
if (object instanceof Cacheable) {
return ((Cacheable)object).getCachedSize();
}
// Check for other common types of objects put into cache.
else if (object instanceof String) {
return CacheSizes.sizeOfString((String)object);
}
else if (object instanceof Long) {
return CacheSizes.sizeOfLong();
}
else if (object instanceof Integer) {
return CacheSizes.sizeOfObject() + CacheSizes.sizeOfInt();
}
else if (object instanceof Boolean) {
return CacheSizes.sizeOfObject() + CacheSizes.sizeOfBoolean();
}
else if (object instanceof long[]) {
long[] array = (long[])object;
return CacheSizes.sizeOfObject() + array.length * CacheSizes.sizeOfLong();
}
else if (object instanceof byte[]) {
byte [] array = (byte[])object;
return CacheSizes.sizeOfObject() + array.length;
}
// Default behavior -- serialize the object to determine its size.
else {
int size = 1;
try {
// Default to serializing the object out to determine size.
DefaultCache.NullOutputStream out = new DefaultCache.NullOutputStream();
ObjectOutputStream outObj = new ObjectOutputStream(out);
outObj.writeObject(object);
size = out.size();
}
catch (IOException ioe) {
Log.error(ioe.getMessage(), ioe);
}
return size;
}
}
/** /**
* Clears all entries out of cache where the entries are older than the * Clears all entries out of cache where the entries are older than the
* maximum defined age. * maximum defined age.
...@@ -712,37 +666,4 @@ public class DefaultCache<K, V> implements Cache<K, V> { ...@@ -712,37 +666,4 @@ public class DefaultCache<K, V> implements Cache<K, V> {
this.size = size; this.size = size;
} }
} }
/**
* An extension of OutputStream that does nothing but calculate the number
* of bytes written through it.
*/
private static class NullOutputStream extends OutputStream {
int size = 0;
@Override
public void write(int b) throws IOException {
size++;
}
@Override
public void write(byte[] b) throws IOException {
size += b.length;
}
@Override
public void write(byte[] b, int off, int len) {
size += len;
}
/**
* Returns the number of bytes written out through the stream.
*
* @return the number of bytes written to the stream.
*/
public int size() {
return size;
}
}
} }
...@@ -44,11 +44,16 @@ ...@@ -44,11 +44,16 @@
Clustering Plugin Changelog Clustering Plugin Changelog
</h1> </h1>
<p><b>1.2.1</b> -- Dec 16, 2011</p>
<ul>
<li>CoherenceCache is now compatible with a changed API of Openfire caches.</li>
</ul>
<p><b>1.2.0</b> -- Nov 10, 2009</p> <p><b>1.2.0</b> -- Nov 10, 2009</p>
<ul> <ul>
<li>Changed license to Apache 2.0. Oracle Coherence should now be obtained separately.</li> <li>Changed license to Apache 2.0. Oracle Coherence should now be obtained separately.</li>
<li>ClustserListener is now compatible with distributed caches.</li> <li>ClusterListener is now compatible with distributed caches.</li>
</ul> </ul>
<p><b>1.1.1</b> -- June 4, 2008</p> <p><b>1.1.1</b> -- June 4, 2008</p>
......
...@@ -5,8 +5,8 @@ ...@@ -5,8 +5,8 @@
<name>${plugin.name}</name> <name>${plugin.name}</name>
<description>${plugin.description}</description> <description>${plugin.description}</description>
<author>Jive Software</author> <author>Jive Software</author>
<version>1.2.0</version> <version>1.2.1</version>
<date>11/10/2009</date> <date>12/17/2011</date>
<minServerVersion>3.5.2</minServerVersion> <minServerVersion>3.7.2</minServerVersion>
</plugin> </plugin>
...@@ -27,6 +27,7 @@ import org.jivesoftware.util.Log; ...@@ -27,6 +27,7 @@ import org.jivesoftware.util.Log;
import org.jivesoftware.util.cache.Cache; import org.jivesoftware.util.cache.Cache;
import org.jivesoftware.util.cache.CacheSizes; import org.jivesoftware.util.cache.CacheSizes;
import org.jivesoftware.util.cache.Cacheable; import org.jivesoftware.util.cache.Cacheable;
import org.jivesoftware.util.cache.CannotCalculateSizeException;
/** /**
* Implementation of the Cache interface that uses the Coherence LocalCache class. * Implementation of the Cache interface that uses the Coherence LocalCache class.
...@@ -153,34 +154,19 @@ public class CoherenceCache extends LocalCache implements Cache { ...@@ -153,34 +154,19 @@ public class CoherenceCache extends LocalCache implements Cache {
public class Entry extends OldCache.Entry { public class Entry extends OldCache.Entry {
public int calculateUnits(Object object) { public int calculateUnits(Object object) {
// If the object is Cacheable, ask for its size. try {
if (object instanceof Cacheable) { // Coherence puts com.tangosol.util.Binary objects in cache.
return ((Cacheable) object).getCachedSize(); if (object instanceof com.tangosol.util.Binary) {
} return ((com.tangosol.util.Binary) object).length();
// Coherence puts com.tangosol.util.Binary objects in cache. }
else if (object instanceof com.tangosol.util.Binary) { // Check for other common types of objects put into cache.
return ((com.tangosol.util.Binary) object).length(); else {
} return CacheSizes.sizeOfAnything(object);
// Check for other common types of objects put into cache. }
else if (object instanceof Long) { } catch (CannotCalculateSizeException e) {
return CacheSizes.sizeOfLong(); Log.warn(e.getMessage(), e);
}
else if (object instanceof Integer) {
return CacheSizes.sizeOfObject() + CacheSizes.sizeOfInt();
}
else if (object instanceof Boolean) {
return CacheSizes.sizeOfObject() + CacheSizes.sizeOfBoolean();
}
else if (object instanceof long[]) {
long[] array = (long[]) object;
return CacheSizes.sizeOfObject() + array.length * CacheSizes.sizeOfLong();
}
else if (object instanceof String) {
return CacheSizes.sizeOfString((String)object);
}
else {
return 1; return 1;
} }
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment