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.object;
006:
007: import EDU.oswego.cs.dl.util.concurrent.ConcurrentHashMap;
008:
009: import com.tc.aspectwerkz.reflect.impl.java.JavaClassInfo;
010: import com.tc.object.bytecode.TransparentAccess;
011: import com.tc.object.config.DSOClientConfigHelper;
012: import com.tc.util.Assert;
013: import com.tc.util.ClassUtils;
014: import com.tc.util.NonPortableReason;
015:
016: import java.lang.reflect.Proxy;
017: import java.util.ArrayList;
018: import java.util.Iterator;
019: import java.util.List;
020: import java.util.Map;
021:
022: public class PortabilityImpl implements Portability {
023:
024: private static final Class OBJECT_CLASS = Object.class;
025: private static final List nonInstrumentedClass = new ArrayList();
026:
027: static {
028: nonInstrumentedClass.add("java.lang.Object");
029: nonInstrumentedClass.add("java.lang.Number");
030: nonInstrumentedClass.add("java.util.AbstractList");
031: nonInstrumentedClass.add("java.util.AbstractCollection");
032: nonInstrumentedClass.add("java.util.AbstractQueue");
033: nonInstrumentedClass.add("java.util.Dictionary");
034: nonInstrumentedClass.add("java.lang.Enum");
035: nonInstrumentedClass.add("java.lang.reflect.AccessibleObject");
036: nonInstrumentedClass
037: .add("java.util.concurrent.atomic.AtomicInteger");
038: nonInstrumentedClass
039: .add("java.util.concurrent.atomic.AtomicLong");
040: // nonInstrumentedClass.add("java.util.concurrent.locks.AbstractQueuedSynchronizer");
041: // nonInstrumentedClass.add("java.util.concurrent.locks.AbstractQueuedSynchronizer$Node");
042: }
043:
044: private final LiteralValues literalValues = new LiteralValues();
045: private final Map portableCache = new ConcurrentHashMap();
046: private final Map physicalCache = new ConcurrentHashMap();
047: private final DSOClientConfigHelper config;
048:
049: public PortabilityImpl(DSOClientConfigHelper config) {
050: this .config = config;
051: }
052:
053: private List getHierarchy(Class start) {
054: List classes = new ArrayList();
055: while (start != null && start != OBJECT_CLASS) {
056: classes.add(start);
057: start = start.getSuperclass();
058: }
059: return classes;
060: }
061:
062: public NonPortableReason getNonPortableReason(
063: final Class topLevelClass) {
064: final List classes = getHierarchy(topLevelClass);
065:
066: // check if any class in the class hierarchy is not-adaptable (like java.lang.Thread etc.)
067: for (Iterator i = classes.iterator(); i.hasNext();) {
068: Class class2Inspect = (Class) i.next();
069: if (config.isNeverAdaptable(JavaClassInfo
070: .getClassInfo(class2Inspect))) {
071: if (class2Inspect == topLevelClass) {
072: return new NonPortableReason(topLevelClass,
073: NonPortableReason.CLASS_NOT_ADAPTABLE);
074: } else {
075: NonPortableReason reason = new NonPortableReason(
076: topLevelClass,
077: NonPortableReason.SUPER_CLASS_NOT_ADAPTABLE);
078: reason.addErroneousSuperClass(class2Inspect);
079: return reason;
080: }
081: }
082: }
083:
084: // check for the set of types that weren't instrumented
085: byte reasonCode = NonPortableReason.UNDEFINED;
086: List uninstrumentedSupers = new ArrayList();
087: for (Iterator i = classes.iterator(); i.hasNext();) {
088: Class class2Inspect = (Class) i.next();
089: if (class2Inspect == topLevelClass) {
090: if (!isPortableClass(class2Inspect)) {
091: Assert
092: .assertTrue(reasonCode == NonPortableReason.UNDEFINED);
093: if (class2Inspect.getClassLoader() == null) {
094: reasonCode = NonPortableReason.CLASS_NOT_IN_BOOT_JAR;
095: } else {
096: reasonCode = NonPortableReason.CLASS_NOT_INCLUDED_IN_CONFIG;
097: }
098: }
099: } else {
100: if (!isPortableClass(class2Inspect)) {
101: if (reasonCode == NonPortableReason.UNDEFINED
102: || config.getSpec(topLevelClass.getName()) != null) {
103: reasonCode = NonPortableReason.SUPER_CLASS_NOT_INSTRUMENTED;
104: }
105: uninstrumentedSupers.add(class2Inspect);
106: }
107: }
108: }
109:
110: if (uninstrumentedSupers.size() > 0
111: || reasonCode == NonPortableReason.CLASS_NOT_IN_BOOT_JAR) {
112: NonPortableReason reason = new NonPortableReason(
113: topLevelClass, reasonCode);
114: for (Iterator i = uninstrumentedSupers.iterator(); i
115: .hasNext();) {
116: reason.addErroneousSuperClass((Class) i.next());
117: }
118: return reason;
119: }
120:
121: // Now check if it is a subclass of logically managed class
122: for (Iterator i = classes.iterator(); i.hasNext();) {
123: Class class2Inspect = (Class) i.next();
124:
125: // if a parent class simply wasn't included, don't report this a logical subclass issue until it really is
126: if (!config.shouldBeAdapted(JavaClassInfo
127: .getClassInfo(class2Inspect))) {
128: break;
129: }
130:
131: if (config.isLogical(class2Inspect.getName())) {
132: NonPortableReason reason = new NonPortableReason(
133: topLevelClass,
134: NonPortableReason.SUBCLASS_OF_LOGICALLY_MANAGED_CLASS);
135: reason.addErroneousSuperClass(class2Inspect);
136: return reason;
137: }
138: }
139:
140: return new NonPortableReason(topLevelClass, reasonCode);
141: }
142:
143: /*
144: * This method does not rely on the config but rather on the fact that the class has to be instrumented at this time
145: * for the object to be portable. For Logical Objects it still queries the config.
146: */
147: public boolean isPortableClass(Class clazz) {
148: String clazzName = clazz.getName();
149: Boolean isPortable = (Boolean) portableCache.get(clazzName);
150: if (isPortable != null) {
151: return isPortable.booleanValue();
152: }
153:
154: boolean bool = literalValues.isLiteral(clazzName)
155: || config.isLogical(clazzName) || clazz.isArray()
156: || Proxy.isProxyClass(clazz)
157: || ClassUtils.isEnum(clazz)
158: || isClassPhysicallyInstrumented(clazz)
159: || isInstrumentationNotNeeded(clazzName)
160: || ClassUtils.isPortableReflectionClass(clazz)
161: || config.isPortableModuleClass(clazz);
162: portableCache.put(clazzName, Boolean.valueOf(bool));
163: return bool;
164: }
165:
166: public boolean isInstrumentationNotNeeded(String clazzName) {
167: return nonInstrumentedClass.contains(clazzName);
168: }
169:
170: public boolean isClassPhysicallyInstrumented(Class clazz) {
171: // this method should only return true if this class "directly" implements
172: // the interface in question. It specifically does *NOT* walk the class hierarchy looking
173: // for the interface. This always means you can't just say instanceof here
174:
175: String clazzName = clazz.getName();
176: Boolean isPhysicalAdapted = (Boolean) physicalCache
177: .get(clazzName);
178: if (isPhysicalAdapted != null) {
179: return isPhysicalAdapted.booleanValue();
180: }
181:
182: boolean rv = false;
183: Class interfaces[] = clazz.getInterfaces();
184: if (interfaces == null || interfaces.length == 0)
185: return false;
186: for (int i = 0; i < interfaces.length; i++) {
187: if (interfaces[i] == TransparentAccess.class) {
188: rv = true;
189: break;
190: }
191: }
192:
193: physicalCache.put(clazzName, Boolean.valueOf(rv));
194: return rv;
195: }
196:
197: public boolean isPortableInstance(Object obj) {
198: if (obj == null)
199: return true;
200: return isPortableClass(obj.getClass());
201: }
202: }
|