001: /*
002: * All content copyright (c) 2003-2007 Terracotta, Inc., except as may otherwise be noted in a separate copyright
003: * notice. All rights reserved.
004: */
005: package com.tctest;
006:
007: import org.apache.commons.io.IOUtils;
008: import org.apache.commons.lang.ClassUtils;
009:
010: import com.tc.exception.TCNonPortableObjectError;
011: import com.tc.logging.CustomerLogging;
012: import com.tc.logging.LogLevel;
013: import com.tc.logging.TCAppender;
014: import com.tc.logging.TCLogging;
015: import com.tc.object.bytecode.Manageable;
016: import com.tc.object.config.ConfigVisitor;
017: import com.tc.object.config.DSOClientConfigHelper;
018: import com.tc.object.config.TransparencyClassSpec;
019: import com.tc.simulator.app.ApplicationConfig;
020: import com.tc.simulator.listener.ListenerProvider;
021: import com.tc.util.Assert;
022: import com.tctest.runner.AbstractErrorCatchingTransparentApp;
023:
024: import java.io.ByteArrayOutputStream;
025: import java.io.IOException;
026: import java.io.InputStream;
027: import java.net.ServerSocket;
028: import java.util.ArrayList;
029: import java.util.Iterator;
030: import java.util.LinkedHashMap;
031: import java.util.List;
032: import java.util.Map;
033:
034: public class NonPortableInstancesTest extends TransparentTestBase {
035:
036: private static final int NODE_COUNT = 1;
037:
038: public void doSetUp(TransparentTestIface t) throws Exception {
039: t.getTransparentAppConfig().setClientCount(NODE_COUNT);
040: t.initializeTestRunner();
041: }
042:
043: protected Class getApplicationClass() {
044: return App.class;
045: }
046:
047: public static class App extends AbstractErrorCatchingTransparentApp {
048:
049: private final Object[] array = new Object[1];
050: private final Ref physicalObject = new Ref();
051: private final List logicalObject = new ArrayList();
052: private Object nonPortableRoot;
053:
054: private final Map map = new LinkedHashMap();
055:
056: private final LogAppender logEvents;
057:
058: public App(String appId, ApplicationConfig cfg,
059: ListenerProvider listenerProvider) {
060: super (appId, cfg, listenerProvider);
061:
062: logEvents = new LogAppender();
063:
064: TCLogging.addAppender(CustomerLogging.getDSORuntimeLogger()
065: .getName(), logEvents);
066: }
067:
068: public Object getNonPortableRoot() {
069: // this method here to silence compiler warning
070: return nonPortableRoot;
071: }
072:
073: protected void runTest() throws Throwable {
074:
075: // array elements are checked for portability before traversing
076: try {
077: synchronized (array) {
078: array[0] = new NotPortable();
079: }
080: throw new AssertionError();
081: } catch (TCNonPortableObjectError e) {
082: // expected
083: }
084: validate(1);
085:
086: // field sets are checked for portability before traversing
087: try {
088: synchronized (physicalObject) {
089: physicalObject.setRef(new NotPortable());
090: }
091: throw new AssertionError();
092: } catch (TCNonPortableObjectError e) {
093: // expected
094: }
095: validate(1);
096:
097: // params to methods on logical types are checked for portability before traversing
098: try {
099: synchronized (logicalObject) {
100: logicalObject.add(new NotPortable());
101: }
102: throw new AssertionError();
103: } catch (TCNonPortableObjectError e) {
104: // expected
105: }
106: validate(1);
107:
108: // root values are checked for portability before traversing
109: try {
110: nonPortableRoot = new NotPortable();
111: throw new AssertionError();
112: } catch (TCNonPortableObjectError e) {
113: // expected
114: }
115: validate(1);
116:
117: // This test will pass the initial portability checks (both params to put() are portable), but the value object
118: // contains a reference to a non-portable type
119: try {
120: synchronized (map) {
121: map.put("key", new Portable());
122: }
123: throw new AssertionError();
124: } catch (TCNonPortableObjectError e) {
125: // expected
126: }
127: validate(2);
128:
129: }
130:
131: private void validate(int i) throws IOException {
132: String expect = getExpected(i);
133: String actual = logEvents.takeLoggedMessages();
134:
135: expect = expect.replaceAll("\r", "");
136: actual = actual.replaceAll("\r", "");
137:
138: Assert.assertEquals(expect, actual);
139: }
140:
141: private String getExpected(int i) throws IOException {
142: String resource = ClassUtils.getShortClassName(getClass())
143: + "-dump" + i + ".txt";
144: ByteArrayOutputStream baos = new ByteArrayOutputStream();
145:
146: InputStream in = null;
147: try {
148: in = getClass().getResourceAsStream(resource);
149: if (in == null) {
150: fail("missing resource: " + resource);
151: }
152: IOUtils.copy(in, baos);
153: } finally {
154: if (in != null) {
155: try {
156: in.close();
157: } catch (Exception e) {
158: throw new RuntimeException(e);
159: }
160: }
161: }
162:
163: baos.flush();
164: return new String(baos.toByteArray());
165: }
166:
167: public static void visitL1DSOConfig(ConfigVisitor visitor,
168: DSOClientConfigHelper config) {
169: config.getOrCreateSpec(Ref.class.getName());
170: TransparencyClassSpec spec = config
171: .getOrCreateSpec(Portable.class.getName());
172: spec.setHonorTransient(true);
173: spec.addTransient("ss");
174:
175: String testClass = App.class.getName();
176: spec = config.getOrCreateSpec(testClass);
177: String methodExpr = "* " + testClass + "*.*(..)";
178: config.addWriteAutolock(methodExpr);
179:
180: spec.addRoot("logicalObject", "logicalObject");
181: spec.addRoot("array", "array");
182: spec.addRoot("physicalObject", "physicalObject");
183: spec.addRoot("nonPortableRoot", "nonPortableRoot");
184:
185: spec.addRoot("map", "map");
186: }
187:
188: }
189:
190: private static class Ref {
191: private Object ref;
192:
193: Object getRef() {
194: return ref;
195: }
196:
197: void setRef(Object ref) {
198: this .ref = ref;
199: }
200:
201: }
202:
203: private static class Portable {
204: Ref ref = new Ref();
205:
206: transient Runtime honeredTransient = Runtime.getRuntime();
207: ServerSocket ss; // transient by DSO config
208:
209: final Thread nullThread1 = null;
210: final Thread nullThread2 = null;
211:
212: Portable() {
213: ref
214: .setRef(makeGraphWithNonPortableNodes(new NotPortable()));
215: try {
216: ss = new ServerSocket();
217: } catch (IOException e) {
218: throw new AssertionError(e);
219: }
220: }
221: }
222:
223: private static class NotPortable {
224: final Map m = makeGraphWithNonPortableNodes(this );
225:
226: Thread t = Thread.currentThread();
227:
228: NotPortable() {
229: if (this instanceof Manageable) {
230: throw new AssertionError(
231: "this type should not be portable");
232: }
233: }
234: }
235:
236: private static Map makeGraphWithNonPortableNodes(Object nonPortable) {
237: Map m = new LinkedHashMap();
238: Ref ref = new Ref();
239: Ref r2 = new Ref();
240: r2.setRef(nonPortable);
241: ref.setRef(r2);
242:
243: m.put("ref", ref);
244:
245: Object[][] a = new Object[][] { { null }, { new Ref() } };
246: ((Ref) a[1][0]).setRef(m);
247:
248: m.put("array", a);
249:
250: return m;
251: }
252:
253: private static class LogAppender implements TCAppender {
254:
255: private final List events = new ArrayList();
256:
257: public void append(LogLevel level, Object message,
258: Throwable throwable) {
259: events.add(new Event(level, message, throwable));
260: }
261:
262: String takeLoggedMessages() {
263: StringBuffer buf = new StringBuffer();
264: for (Iterator iter = events.iterator(); iter.hasNext();) {
265: Event event = (Event) iter.next();
266: buf.append(event.getMessage() + "\n");
267: }
268:
269: events.clear();
270:
271: return buf.toString();
272: }
273:
274: }
275:
276: static class Event {
277: private final LogLevel level;
278: private final Object message;
279: private final Throwable throwable;
280:
281: Event(LogLevel level, Object message, Throwable throwable) {
282: this .level = level;
283: this .message = message;
284: this .throwable = throwable;
285: }
286:
287: public LogLevel getLevel() {
288: return level;
289: }
290:
291: public Object getMessage() {
292: return message;
293: }
294:
295: public Throwable getThrowable() {
296: return throwable;
297: }
298:
299: public String toString() {
300: return "["
301: + level
302: + "] "
303: + message
304: + ((throwable == null) ? "" : throwable
305: .getMessage());
306: }
307: }
308:
309: }
|