I am trying to develop some utility code which is portable to both the J2SE and the Android platforms for the caching of objects. The plan is to extract an abstract class when I get it working, but for now it is part of the final class implementation.
The object I wish to cache has just be instantiated and passed to the store(Object, Object)
method that will become part of abstract class in the final version, so it needs to use introspection to determine how to store the data it's been handed.
The algorithm isn't very complicated:
1). Scan the object's methods looking for the @Id
attribute, and if found use it to fetch the object's key.
2). Scan the store() method's own class for a field that implements the put(key.getClass(), value.getClass())
method and use that to cache the object.
Part (1) works just fine, it's part (2) that I can't get to work. I get:
DEBUG c.n.v.ui.cache.Cache Not found: operativesMap.put(String, OperativeEntity)
WARN c.n.v.ui.cache.Cache Object Joe Bloogs, with key cac57510-c9c6-11df-8000-b56f1ffcb182 not cached!
INFO c.n.v.ui.cache.Cache Object Joe Bloggs, with key cac57510-c9c6-11df-8000-b56f1ffcb182 cached.
logged showing that the introspection code has failed to find the put method and has fallen though to the double check logic (which will have to be removed when I move that method to the abstract class).
As always many thanks for you kind help in this.
Steve
public void store(Object key,
Object value) {
if (log.isInfoEnabled())
log.info("Storing: " + key + " => " + value);
for (Field field: getClass().getDeclaredFields())
if (store(field, key, value))
return;
log.warn("Object " + value + ", with key " + key + " not cached!");
if (value instanceof OperativeEntity) {
operativesMap.put(key.toString(), (OperativeEntity) value);
log.info("Object " + value + ", with key " + key + " cached.");
// How did I get here, and I do - the loop above should have found
// operativesMap, it's put method and stored the value
}
}
public boolean store(Field field,
Object key,
Object value) {
try {
Method method = field.getClass().getMethod("put", key.getClass(), value.getClass());
if (null != method)
return store(field.get(this), method, key, value);
else
return false;
} catch (NoSuchMethodException cause) {
if (log.isDebugEnabled())
log.debug("Not found: " + field.getName() + ".put(" + key.getClass().getSimpleName()
+ ", " + value.getClass().getSimpleName() + ")");
return false;
} catch (Exception cause) {
log.error("Stroing " + key + "=>" + value + " mapping", cause);
return false;
}
}
public boolean store(Object obj,
Method method,
Object key,
Object value) {
try {
method.invoke(obj, key, value);
if (log.isInfoEnabled())
log.info(">>>>>> Cached " + key + "=>" + value);
return true;
} catch (Exception cause) {
log.error("Stroing " + key + "=>" + value + " mapping", cause);
return false;
}
}
Update:
For anyone reading this in the future, the code for finding the put method that works is given below:
public boolean store(Field field,
Object key,
Object value) {
try {
Object obj = field.get(this);
Method method = obj.getClass().getMethod("put", Object.class, Object.class);
if (null != method) {
Class[] params = method.getParameterTypes();
if (log.isDebugEnabled())
log.debug(" Found: put(" + params[0].getSimpleName() + ", " + params[1].getSimpleName() + ")");
if (params[0].isAssignableFrom(key.getClass())
&& params[1].isAssignableFrom(value.getClass()))
return store(obj, method, key, value);
else
return false;
} else
return false;
} catch (NoSuchMethodException cause) {
if (log.isDebugEnabled())
log.debug("Not found: " + field.getName() + ".put(Object, Object)");
return false;
} catch (Exception cause) {
log.error("Storing " + key + "=>" + value + " mapping", cause);
return false;
}
}
Class.getMethod()
takes the actual declared types of the parameters. So, for a map, that would be getMethod("put", Object.class, Object.class)
. If you just want to find a put()
method that can accept the key and value type you have, you will need to iterate through the methods of the class and check the parameters with Class.isAssignableFrom()
.