001: /*
002: * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright notice. All rights reserved.
003: */
004: package com.tctest;
005:
006: import org.apache.commons.io.IOUtils;
007:
008: import com.tc.object.config.ConfigVisitor;
009: import com.tc.object.config.DSOClientConfigHelper;
010: import com.tc.object.config.TransparencyClassSpec;
011: import com.tc.process.LinkedJavaProcess;
012: import com.tc.process.StreamCollector;
013: import com.tc.simulator.app.ApplicationConfig;
014: import com.tc.simulator.listener.ListenerProvider;
015: import com.tc.util.Util;
016: import com.tctest.runner.AbstractTransparentApp;
017:
018: import java.io.ByteArrayInputStream;
019: import java.io.ByteArrayOutputStream;
020: import java.io.FileInputStream;
021: import java.io.FileOutputStream;
022: import java.io.IOException;
023: import java.io.InputStream;
024: import java.io.ObjectInputStream;
025: import java.io.ObjectOutputStream;
026: import java.io.ObjectStreamClass;
027: import java.io.OutputStream;
028: import java.io.Serializable;
029: import java.lang.reflect.Field;
030: import java.lang.reflect.Modifier;
031: import java.util.Arrays;
032: import java.util.HashMap;
033: import java.util.Map;
034:
035: public class SerialVersionUIDTestApp extends AbstractTransparentApp {
036:
037: public static final String TEMP_FILE_KEY = "tempFile";
038:
039: private final Map map = new HashMap();
040: private final String fileName;
041:
042: public SerialVersionUIDTestApp(String appId, ApplicationConfig cfg,
043: ListenerProvider listenerProvider) {
044: super (appId, cfg, listenerProvider);
045: this .fileName = cfg.getAttribute(TEMP_FILE_KEY);
046: }
047:
048: public void run() {
049: final boolean first;
050: synchronized (map) {
051: first = map.isEmpty();
052: if (first) {
053: map.put("with", new WithUID());
054: map.put("without", new WithoutUID());
055: }
056: }
057:
058: WithUID with = (WithUID) map.get("with");
059: WithoutUID without = (WithoutUID) map.get("without");
060:
061: checkUID(with, WithUID.serialVersionUID);
062: checkUID(without, WithoutUID.EXPECTED_UID);
063:
064: doTheDance(with);
065: doTheDance(without);
066:
067: verifyAddedField(without);
068:
069: if (first) {
070: // only do this in one node
071: verifyExternal();
072: }
073: }
074:
075: private void verifyAddedField(WithoutUID without) {
076: Field field = getSerialUIDField(without);
077:
078: if (field == null) {
079: throw new RuntimeException(
080: "Could not find the serialVersionUID field: "
081: + Arrays.asList(without.getClass()
082: .getDeclaredFields()));
083: }
084:
085: int access = field.getModifiers();
086: if ((!Modifier.isStatic(access)) || (!Modifier.isFinal(access))) {
087: // make formatter sane
088: throw new RuntimeException("Bad permissions: " + access);
089: }
090:
091: Class type = field.getType();
092: if (!Long.TYPE.equals(type)) {
093: throw new RuntimeException("Bad type: " + type);
094: }
095:
096: // a-okay, just return
097: return;
098: }
099:
100: private void verifyExternal() {
101: // this verifies that we can share classes (via serialization) with a VM that doesn't use DSO instrumentation
102:
103: byte[] dataIn = null;
104:
105: try {
106: OutputStream out = new FileOutputStream(fileName + ".in",
107: false);
108: out.write(serialize(new WithoutUID()));
109: out.close();
110:
111: LinkedJavaProcess process = new LinkedJavaProcess(
112: ExternalSerialize.class.getName(),
113: new String[] { fileName });
114: process.start();
115:
116: process.STDIN().close();
117: StreamCollector stdout = new StreamCollector(process
118: .STDOUT());
119: stdout.start();
120: StreamCollector stderr = new StreamCollector(process
121: .STDERR());
122: stderr.start();
123:
124: int exitCode = process.waitFor();
125:
126: stdout.join();
127: stderr.join();
128:
129: if (exitCode != 0) {
130: throw new RuntimeException("Process exited with code "
131: + exitCode + ", stdout: " + stdout.toString()
132: + ", stderr: " + stderr);
133: }
134:
135: InputStream in = new FileInputStream(fileName + ".out");
136: dataIn = IOUtils.toByteArray(in);
137: if (dataIn.length == 0) {
138: throw new RuntimeException("No data read");
139: }
140: in.close();
141: deserialize(dataIn);
142: } catch (Exception e) {
143: throw new RuntimeException(Util.enumerateArray(dataIn), e);
144: }
145: }
146:
147: private static Field getSerialUIDField(Object obj) {
148: Field[] fields = obj.getClass().getDeclaredFields();
149: for (int i = 0; i < fields.length; i++) {
150: Field f = fields[i];
151: if ("serialVersionUID".equals(f.getName())) {
152: return f;
153: }
154: }
155: return null;
156: }
157:
158: private void doTheDance(Object obj) {
159: // this doesn't really test all that much, but it makes sure that the given object can be serialized and
160: // deserialized
161: try {
162: byte[] data = serialize(obj);
163: deserialize(data);
164: } catch (Exception e) {
165: notifyError(e);
166: }
167: }
168:
169: private static Object deserialize(byte[] data) throws IOException,
170: ClassNotFoundException {
171: ObjectInputStream ois = new ObjectInputStream(
172: new ByteArrayInputStream(data));
173: Object rv = ois.readObject();
174: ois.close();
175: return rv;
176: }
177:
178: private static byte[] serialize(Object obj) throws IOException {
179: ByteArrayOutputStream baos = new ByteArrayOutputStream();
180: ObjectOutputStream oos = new ObjectOutputStream(baos);
181: oos.writeObject(obj);
182: oos.close();
183: return baos.toByteArray();
184: }
185:
186: private void checkUID(Object obj, long expect) {
187: long uid = ObjectStreamClass.lookup(obj.getClass())
188: .getSerialVersionUID();
189: if (uid != expect) {
190: throw new RuntimeException("Unexpected UID: " + uid
191: + ", expected " + expect);
192: }
193: }
194:
195: public static void visitL1DSOConfig(ConfigVisitor visitor,
196: DSOClientConfigHelper config) {
197: String testClass = SerialVersionUIDTestApp.class.getName();
198: TransparencyClassSpec spec = config.getOrCreateSpec(testClass);
199: spec.addRoot("map", "map");
200:
201: String methodExpression = "* " + testClass + ".*(..)";
202: config.addWriteAutolock(methodExpression);
203:
204: config.addIncludePattern(testClass + "$*");
205:
206: config.getOrCreateSpec(WithoutUID.class.getName());
207: }
208:
209: private static class WithUID implements Serializable {
210: static final long serialVersionUID = 0xDECAFBAD;
211: }
212:
213: public static class ExternalSerialize {
214:
215: public static void main(String args[]) throws Exception {
216: try {
217: if (args.length != 1) {
218: error("invalid number of args " + args.length);
219: }
220:
221: String file = args[0];
222:
223: FileInputStream in = new FileInputStream(file + ".in");
224: byte[] dataIn = IOUtils.toByteArray(in);
225: in.close();
226:
227: Object o = deserialize(dataIn);
228:
229: verifyNoSerialUID(o);
230:
231: FileOutputStream out = new FileOutputStream(file
232: + ".out", false);
233: out.write(serialize(o));
234: out.flush();
235: out.close();
236: System.exit(0);
237: } catch (Throwable t) {
238: t.printStackTrace();
239: error(t.getMessage());
240: }
241:
242: }
243:
244: private static void verifyNoSerialUID(Object o) {
245: Field f = getSerialUIDField(o);
246: if (f != null) {
247: throw new RuntimeException(
248: "Class has a serialVersionUID field: " + f);
249: }
250: }
251:
252: private static void error(String msg) {
253: System.err.println(msg);
254: System.err.flush();
255: System.exit(1);
256: throw new RuntimeException(msg);
257: }
258: }
259:
260: }
|