org.springsource.loaded.SSMgr Maven / Gradle / Ivy
/*
* Copyright 2010-2012 VMware and contributors
*
* 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.springsource.loaded;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Static State Manager. The top most class in every hierarchy of reloadable types gets a static state manager instance. The static
* state manager is used to find the value of a field for a particular object instance. The FieldAccessor is added to the top most
* type in a reloadable hierarchy and is accessible to all the subtypes. It maintains a map from type names to fields (name/value
* pairs).
*
* @author Andy Clement
* @since 0.5.0
*/
public class SSMgr {
private static Logger log = Logger.getLogger(SSMgr.class.getName());
Map> values = new HashMap>();
public Object getValue(ReloadableType rtype, String name) throws IllegalAccessException {
// System.out.println("SSMgr.getValue(rtype=" + rtype + ",name=" + name + ")");
Object result = null;
// quick look to see if it is nearby (searches up supertype hierarchy, but only up to the topmost reloadabletype)
FieldMember fieldmember = rtype.findStaticField(name);//InstanceField(name);
// Why can fieldmember be null?
// 1. Field really does not exist - shouldn't really be possible if the code is 'valid'
// 2. Field is inherited from a supertype (usually because a reload has occurred)
if (fieldmember == null) {
FieldReaderWriter flr = rtype.locateField(name);
if (flr == null) {
log.info("Unexpectedly unable to locate static field " + name + " starting from type " + rtype.dottedtypename
+ ": clinit running late?");
return null;
}
result = flr.getStaticFieldValue(rtype.getClazz(), this);
} else {
if (!fieldmember.isStatic()) {
throw new IncompatibleClassChangeError("Expected static field " + rtype.dottedtypename + "."
+ fieldmember.getName());
}
String declaringTypeName = fieldmember.getDeclaringTypeName();
Map typeLevelValues = values.get(declaringTypeName);
boolean knownField = false;
if (typeLevelValues != null) {
knownField = typeLevelValues.containsKey(name);
}
if (knownField) {
result = typeLevelValues.get(name);
}
// If a field has been deleted it may 'reveal' a field in a supertype. The revealed field may be in a type
// not yet dealt with. In this case typeLevelValues may be null (type not seen before) or the typelevelValues
// may not have heard of our field name. In these cases we need to go and find the field and 'relocate' it
// into our map, where it will be processed from now on.
// These revealed fields are not necessarily in the original form of the type so cannot always be accessed via reflection
if (typeLevelValues == null || !knownField) {
// TODO lookup performance?
FieldMember fieldOnOriginalType = rtype.getTypeRegistry().getReloadableType(declaringTypeName).getTypeDescriptor()
.getField(name);
if (fieldOnOriginalType != null) {
// Copy that field into the map... where it is going to live from now on
ReloadableType rt = rtype.getTypeRegistry().getReloadableType(fieldmember.getDeclaringTypeName());
try {
Field f = rt.getClazz().getDeclaredField(name);
f.setAccessible(true);
result = f.get(null);
if (typeLevelValues == null) {
typeLevelValues = new HashMap();
values.put(declaringTypeName, typeLevelValues);
}
typeLevelValues.put(name, result);
} catch (Exception e) {
throw new IllegalStateException("Unexpectedly unable to access field " + name + " on type "
+ rt.getClazz().getName(), e);
}
} else {
// The field was not on the original type. As not seen before, can default it
result = Utils.toResultCheckIfNull(null, fieldmember.getDescriptor());
if (typeLevelValues == null) {
typeLevelValues = new HashMap();
values.put(declaringTypeName, typeLevelValues);
}
typeLevelValues.put(name, result);
return result;
}
}
if (result != null) {
result = Utils.checkCompatibility(rtype.getTypeRegistry(), result, fieldmember.getDescriptor());
if (result == null) {
typeLevelValues.remove(fieldmember.getName());
}
}
result = Utils.toResultCheckIfNull(result, fieldmember.getDescriptor());
}
// if (GlobalConfiguration.isRuntimeLogging && log.isLoggable(Level.FINER)) {
// log.finer(" typeValues = values.get(fieldmember.getDeclaringTypeName());//rtype.getName());
if (typeValues == null) {
typeValues = new HashMap();
values.put(fieldmember.getDeclaringTypeName(), typeValues);
}
typeValues.put(name, newValue);
}
}
private String valuesToString() {
StringBuilder s = new StringBuilder();
s.append("FieldAccessor:" + System.identityHashCode(this)).append("\n");
for (Map.Entry> entry : values.entrySet()) {
s.append("Type " + entry.getKey()).append("\n");
for (Map.Entry entry2 : entry.getValue().entrySet()) {
s.append(" " + entry2.getKey() + "=" + entry2.getValue()).append("\n");
}
}
return s.toString();
}
public String toString() {
return valuesToString();
}
Map> getMap() {
return values;
}
}