001: /*
002: * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright
003: * notice. All rights reserved.
004: */
005: package com.tc.util;
006:
007: import sun.reflect.FieldAccessor;
008:
009: import com.tc.asm.Type;
010: import com.tc.exception.TCRuntimeException;
011: import com.tc.object.bytecode.ByteCodeUtil;
012: import com.tc.object.bytecode.Manageable;
013: import com.tc.object.bytecode.ManagerUtil;
014: import com.tc.object.bytecode.TransparentAccess;
015:
016: import java.lang.reflect.Field;
017: import java.lang.reflect.InvocationTargetException;
018: import java.lang.reflect.Method;
019: import java.lang.reflect.Modifier;
020:
021: public class FieldUtils {
022: public final static String CLASS = "com/tc/util/FieldUtils";
023:
024: public final static String GET_DESC = "(Ljava/lang/Object;Ljava/lang/reflect/Field;Lsun/reflect/FieldAccessor;)";
025:
026: private static ThreadLocal allowAccess = new ThreadLocal();
027:
028: private static boolean isTCField(Field field) {
029: return field.getName().startsWith(ByteCodeUtil.TC_FIELD_PREFIX);
030: }
031:
032: public static Object get(Object obj, Field field,
033: FieldAccessor fieldAccessor) {
034: if (isTCField(field)) {
035: return null;
036: } else if (!isStaticAndNonRootField(field)) {
037: if (ManagerUtil.isRoot(field)
038: || ManagerUtil.isPhysicallyInstrumented(field
039: .getDeclaringClass())) {
040: if ((obj instanceof TransparentAccess)
041: && !isStaticField(field)) {
042: return resolveReference((TransparentAccess) obj,
043: field);
044: } else {
045: return resolveReference(obj, field);
046: }
047: }
048: }
049: // XXX: disallow field reads of shared logical objects?
050: return fieldAccessor.get(obj);
051: }
052:
053: private static void throwIllegalArgumentException(String type) {
054: StringBuffer sb = new StringBuffer("The argument of type ");
055: sb.append(type);
056: sb.append(" is illegal.");
057: throw new IllegalArgumentException(type);
058: }
059:
060: public static boolean setBoolean(Object obj, boolean value,
061: Field field) throws IllegalAccessException {
062: if (Type.getType(field.getType()).getSort() != Type.BOOLEAN) {
063: throwIllegalArgumentException(field.getType().getName());
064: }
065:
066: return set(obj, new Boolean(value), field);
067: }
068:
069: public static boolean setFloat(Object obj, float value, Field field)
070: throws IllegalAccessException {
071: Type fieldType = Type.getType(field.getType());
072:
073: switch (fieldType.getSort()) {
074: case Type.FLOAT:
075: return set(obj, new Float(value), field);
076: default:
077: return setDouble(obj, value, field);
078: }
079: }
080:
081: public static boolean setDouble(Object obj, double value,
082: Field field) throws IllegalAccessException {
083: if (Type.getType(field.getType()).getSort() != Type.DOUBLE) {
084: throwIllegalArgumentException(field.getType().getName());
085: }
086:
087: return set(obj, new Double(value), field);
088: }
089:
090: public static boolean setChar(Object obj, char value, Field field)
091: throws IllegalAccessException {
092: Type fieldType = Type.getType(field.getType());
093:
094: switch (fieldType.getSort()) {
095: case Type.CHAR:
096: return set(obj, new Character(value), field);
097: default:
098: return setInt(obj, value, field);
099: }
100: }
101:
102: public static boolean setByte(Object obj, byte value, Field field)
103: throws IllegalAccessException {
104: Type fieldType = Type.getType(field.getType());
105:
106: switch (fieldType.getSort()) {
107: case Type.BYTE:
108: return set(obj, new Byte(value), field);
109: default:
110: return setShort(obj, value, field);
111: }
112: }
113:
114: public static boolean setShort(Object obj, short value, Field field)
115: throws IllegalAccessException {
116: Type fieldType = Type.getType(field.getType());
117:
118: switch (fieldType.getSort()) {
119: case Type.SHORT:
120: return set(obj, new Short(value), field);
121: default:
122: return setInt(obj, value, field);
123: }
124: }
125:
126: public static boolean setInt(Object obj, int value, Field field)
127: throws IllegalAccessException {
128: Type fieldType = Type.getType(field.getType());
129:
130: switch (fieldType.getSort()) {
131: case Type.INT:
132: return set(obj, new Integer(value), field);
133: default:
134: return setLong(obj, value, field);
135: }
136: }
137:
138: public static boolean setLong(Object obj, long value, Field field)
139: throws IllegalAccessException {
140: Type fieldType = Type.getType(field.getType());
141:
142: switch (fieldType.getSort()) {
143: case Type.LONG:
144: return set(obj, new Long(value), field);
145: default:
146: return setFloat(obj, value, field);
147: }
148: }
149:
150: /*
151: * This method bypasses our check to set values to shared objects thru reflection. This is used from TC code base when
152: * this is needed. (like subclass of TreeMap, LinkedHashMap cases)
153: */
154: public static void tcSet(Object target, Object value, Field field)
155: throws IllegalArgumentException, IllegalAccessException {
156: allowAccess.set(field);
157: try {
158: field.set(target, value);
159: } finally {
160: allowAccess.set(null);
161: }
162:
163: }
164:
165: private static boolean accessAllowed(Field field) {
166: return field == allowAccess.get();
167: }
168:
169: public static boolean set(Object obj, Object value, Field field)
170: throws IllegalAccessException {
171: if (isTCField(field)) {
172: return true;
173: }
174:
175: if (accessAllowed(field)) {
176: // returning false allows the orignial uninstrumented code to run.
177: return false;
178: }
179:
180: if (isStaticAndNonRootField(field)) {
181: return false;
182: }
183:
184: if (ManagerUtil.isRoot(field)) {
185: if ((obj instanceof TransparentAccess)
186: && !isStaticField(field)) {
187: setValue((TransparentAccess) obj, field, value);
188: } else {
189: // This is an exception handling since we allow defining a field of an non-
190: // instrumented class to be a root.
191: setValue(obj, field, value);
192: }
193: return true;
194: }
195:
196: if ((obj instanceof Manageable)
197: && (((Manageable) obj).__tc_managed() != null)) {
198: if (ManagerUtil.isLogical(obj)) {
199: //
200: throw new IllegalAccessException(
201: "Field modification through reflection for non-physical shared object of type "
202: + obj.getClass().getName()
203: + " is not supported!");
204: }
205:
206: if (!TransparentAccess.class.isAssignableFrom(field
207: .getDeclaringClass())) {
208: //
209: throw new IllegalAccessException(
210: "Field modification through reflection for fields of non-physically instrumented type "
211: + obj.getClass().getName()
212: + " is not supported!");
213: }
214:
215: if (obj instanceof TransparentAccess) {
216: // field of physically managed object
217: setValue((TransparentAccess) obj, field, value);
218: return true;
219: }
220: }
221:
222: return false;
223: }
224:
225: private static boolean isStaticField(Field field) {
226: return Modifier.isStatic(field.getModifiers());
227: }
228:
229: private static boolean isStaticAndNonRootField(Field field) {
230: return isStaticField(field) && !ManagerUtil.isRoot(field);
231: }
232:
233: private static Object resolveReference(TransparentAccess obj,
234: Field field) {
235: // XXX: deal with statics
236: return obj.__tc_getmanagedfield(fullFieldName(field));
237: }
238:
239: private static Object resolveReference(Object obj, Field field) {
240: String fieldGetterMethodName = fieldGetterMethod(field
241: .getName());
242: try {
243: Method m = field.getDeclaringClass().getDeclaredMethod(
244: fieldGetterMethodName, null);
245: m.setAccessible(true);
246: Object retValue = m.invoke(obj, null);
247: return retValue;
248: } catch (NoSuchMethodException e) {
249: throw new TCRuntimeException(e);
250: } catch (InvocationTargetException e) {
251: throw new TCRuntimeException(e);
252: } catch (IllegalArgumentException e) {
253: throw new TCRuntimeException(e);
254: } catch (IllegalAccessException e) {
255: throw new TCRuntimeException(e);
256: }
257: }
258:
259: private static void setValue(Object obj, Field field, Object value) {
260: String fieldSetterMethodName = fieldSetterMethod(field
261: .getName());
262: Class[] setterArgumentsTypes = new Class[] { field.getType() };
263: try {
264: Method m = field.getDeclaringClass().getDeclaredMethod(
265: fieldSetterMethodName, setterArgumentsTypes);
266: m.setAccessible(true);
267: m.invoke(obj, new Object[] { value });
268: } catch (NoSuchMethodException e) {
269: throw new TCRuntimeException(e);
270: } catch (InvocationTargetException e) {
271: throw new TCRuntimeException(e);
272: } catch (IllegalArgumentException e) {
273: throw new TCRuntimeException(e);
274: } catch (IllegalAccessException e) {
275: throw new TCRuntimeException(e);
276: }
277:
278: }
279:
280: private static void setValue(TransparentAccess obj, Field field,
281: Object value) {
282: // XXX: deal with statics
283: obj.__tc_setmanagedfield(fullFieldName(field), value);
284: }
285:
286: private static String fullFieldName(Field field) {
287: return new StringBuffer(field.getDeclaringClass().getName())
288: .append('.').append(field.getName()).toString();
289: }
290:
291: /**
292: * fieldGetterMethod and fieldSetterMethod methods are copied from ByteCodeUtil in order not to put ByteCodeUtil in
293: * the boot jar.
294: */
295: private static String fieldGetterMethod(String fieldName) {
296: return ByteCodeUtil.TC_METHOD_PREFIX + "get" + fieldName;
297: }
298:
299: private static String fieldSetterMethod(String fieldName) {
300: return ByteCodeUtil.TC_METHOD_PREFIX + "set" + fieldName;
301: }
302: }
|