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.lang.ArrayUtils;
007:
008: import EDU.oswego.cs.dl.util.concurrent.CyclicBarrier;
009:
010: import com.tc.object.config.ConfigVisitor;
011: import com.tc.object.config.DSOClientConfigHelper;
012: import com.tc.object.config.TransparencyClassSpec;
013: import com.tc.object.config.spec.CyclicBarrierSpec;
014: import com.tc.object.util.ReadOnlyException;
015: import com.tc.simulator.app.ApplicationConfig;
016: import com.tc.simulator.listener.ListenerProvider;
017: import com.tc.util.Assert;
018: import com.tctest.runner.AbstractErrorCatchingTransparentApp;
019:
020: import gnu.trove.TObjectIntHashMap;
021:
022: import java.lang.reflect.Array;
023: import java.nio.ByteBuffer;
024: import java.security.SecureRandom;
025: import java.util.ArrayList;
026: import java.util.Arrays;
027: import java.util.Collections;
028: import java.util.HashMap;
029: import java.util.Iterator;
030: import java.util.List;
031: import java.util.Map;
032: import java.util.Random;
033:
034: public class PrimitiveArrayTestApp extends
035: AbstractErrorCatchingTransparentApp {
036:
037: private static final int BOOLEAN = 1;
038: private static final int BYTE = 2;
039: private static final int CHAR = 3;
040: private static final int DOUBLE = 4;
041: private static final int FLOAT = 5;
042: private static final int INT = 6;
043: private static final int LONG = 7;
044: private static final int SHORT = 8;
045:
046: private static final int BOOLEAN_WRAPPER = 9;
047: private static final int BYTE_WRAPPER = 10;
048: private static final int CHAR_WRAPPER = 11;
049: private static final int DOUBLE_WRAPPER = 12;
050: private static final int FLOAT_WRAPPER = 13;
051: private static final int INT_WRAPPER = 14;
052: private static final int LONG_WRAPPER = 15;
053: private static final int SHORT_WRAPPER = 16;
054:
055: private static final TObjectIntHashMap classToInt = new TObjectIntHashMap();
056: private static final SecureRandom secure = new SecureRandom();
057:
058: static {
059: classToInt.put(Boolean.TYPE, BOOLEAN);
060: classToInt.put(Byte.TYPE, BYTE);
061: classToInt.put(Character.TYPE, CHAR);
062: classToInt.put(Double.TYPE, DOUBLE);
063: classToInt.put(Float.TYPE, FLOAT);
064: classToInt.put(Integer.TYPE, INT);
065: classToInt.put(Long.TYPE, LONG);
066: classToInt.put(Short.TYPE, SHORT);
067:
068: classToInt.put(Boolean.class, BOOLEAN_WRAPPER);
069: classToInt.put(Byte.class, BYTE_WRAPPER);
070: classToInt.put(Character.class, CHAR_WRAPPER);
071: classToInt.put(Double.class, DOUBLE_WRAPPER);
072: classToInt.put(Float.class, FLOAT_WRAPPER);
073: classToInt.put(Integer.class, INT_WRAPPER);
074: classToInt.put(Long.class, LONG_WRAPPER);
075: classToInt.put(Short.class, SHORT_WRAPPER);
076: }
077:
078: // roots
079: private final DataRoot root = new DataRoot();
080: private final CyclicBarrier barrier;
081:
082: public PrimitiveArrayTestApp(String appId, ApplicationConfig cfg,
083: ListenerProvider listenerProvider) {
084: super (appId, cfg, listenerProvider);
085: this .barrier = new CyclicBarrier(getParticipantCount());
086: }
087:
088: public static void visitL1DSOConfig(ConfigVisitor visitor,
089: DSOClientConfigHelper config) {
090: String testClass = PrimitiveArrayTestApp.class.getName();
091: TransparencyClassSpec spec = config.getOrCreateSpec(testClass);
092:
093: String writeAllowedMethodExpression = "* " + testClass
094: + "*.*(..)";
095: config.addWriteAutolock(writeAllowedMethodExpression);
096:
097: String readOnlyMethodExpression = "* " + testClass
098: + "*.*ReadOnly*(..)";
099: config.addReadAutolock(readOnlyMethodExpression);
100:
101: spec.addRoot("root", "the-data-root-yo");
102: spec.addRoot("barrier", "barrier");
103: config.addIncludePattern(DataRoot.class.getName());
104: new CyclicBarrierSpec().visit(visitor, config);
105: }
106:
107: protected void runTest() throws Throwable {
108: Object array = root.getArray();
109: Class type = array.getClass().getComponentType();
110: Object compare = root.getCompareData(type);
111:
112: // wait for all nodes to get arrays
113: barrier.barrier();
114:
115: // make sure all the data was consumed
116: if (root.getIndex() != root.getNumArrays()) {
117: throw new RuntimeException("Not all data consumed");
118: }
119:
120: // read only tests.
121: readOnlyTest(array);
122:
123: // calling this has the side effect of making sure none of the shared arrays have actually changed yet
124: validate(false);
125:
126: // wait for all read only tests to finish.
127: barrier.barrier();
128:
129: // modify my one type locally
130: modifyDataWithWriteLock(array, intClassType(type));
131:
132: // wait for all nodes to do modifications
133: barrier.barrier();
134:
135: // validate all the changes made by the other nodes
136: validate(true);
137:
138: // wait for all nodes to do validations
139: barrier.barrier();
140:
141: synchronized (array) {
142: // test System.arrayCopy()
143: System.arraycopy(compare, 0, array, 0, Array
144: .getLength(array));
145: }
146:
147: // wait for all nodes to do finish arraycopy (data should be back to original values)
148: barrier.barrier();
149:
150: // validate the arraycopy calls
151: validate(false);
152: }
153:
154: private void readOnlyTest(Object array) {
155: Class type = array.getClass().getComponentType();
156:
157: try {
158: tryModifyDataWithReadOnlyLock(array, intClassType(type));
159: throw new AssertionError(
160: "I should have thrown an ReadOnlyException, type "
161: + type.getName());
162: } catch (ReadOnlyException roe) {
163: // expected
164: }
165: }
166:
167: private void differentDataError(Object a1, Object a2, Class type) {
168: String msg = "Data of type [" + type + "] not equal\n";
169: msg += ArrayUtils.toString(a1) + "\n\n";
170: msg += ArrayUtils.toString(a2);
171: throw new RuntimeException(msg);
172: }
173:
174: private void validate(boolean withMods) {
175: for (Iterator iter = root.getArrayIterator(); iter.hasNext();) {
176: Object actual = iter.next();
177: Class type = actual.getClass().getComponentType();
178: Object expect = root.getCompareData(type);
179:
180: if (withMods) {
181: modifyData(expect, intClassType(type));
182: }
183:
184: if (!compareData(actual, expect)) {
185: differentDataError(actual, expect, type);
186: }
187: }
188: }
189:
190: private static void tryModifyDataWithReadOnlyLock(Object array,
191: int type) {
192: synchronized (array) {
193: modifyData(array, type);
194: }
195: }
196:
197: /*
198: * The synchronized statement inside each individual modify data method such as modifyBoolean() are moved to the
199: * caller method in order to provide both test for modifying data with read only lock and write lock.
200: */
201: private static void modifyDataWithWriteLock(Object array, int type) {
202: synchronized (array) {
203: modifyData(array, type);
204: }
205: }
206:
207: private static void modifyData(Object array, int type) {
208: switch (type) {
209: case BOOLEAN:
210: modifyBoolean((boolean[]) array);
211: break;
212: case BYTE:
213: modifyByte((byte[]) array);
214: break;
215: case CHAR:
216: modifyChar((char[]) array);
217: break;
218: case DOUBLE:
219: modifyDouble((double[]) array);
220: break;
221: case FLOAT:
222: modifyFloat((float[]) array);
223: break;
224: case INT:
225: modifyInt((int[]) array);
226: break;
227: case LONG:
228: modifyLong((long[]) array);
229: break;
230: case SHORT:
231: modifyShort((short[]) array);
232: break;
233: case BOOLEAN_WRAPPER:
234: modifyBooleanWrapper((Boolean[]) array);
235: break;
236: case BYTE_WRAPPER:
237: modifyByteWrapper((Byte[]) array);
238: break;
239: case CHAR_WRAPPER:
240: modifyCharWrapper((Character[]) array);
241: break;
242: case DOUBLE_WRAPPER:
243: modifyDoubleWrapper((Double[]) array);
244: break;
245: case FLOAT_WRAPPER:
246: modifyFloatWrapper((Float[]) array);
247: break;
248: case INT_WRAPPER:
249: modifyIntegerWrapper((Integer[]) array);
250: break;
251: case LONG_WRAPPER:
252: modifyLongWrapper((Long[]) array);
253: break;
254: case SHORT_WRAPPER:
255: modifyShortWrapper((Short[]) array);
256: break;
257: default:
258: throw new RuntimeException("bad type " + type);
259: }
260: }
261:
262: private static void modifyShortWrapper(Short[] s) {
263: for (int i = 0; i < s.length; i++) {
264: s[i] = new Short((short) (s[i].shortValue() + 1));
265: }
266: }
267:
268: private static void modifyLongWrapper(Long[] l) {
269: for (int i = 0; i < l.length; i++) {
270: l[i] = new Long(l[i].longValue() + 1);
271: }
272: }
273:
274: private static void modifyIntegerWrapper(Integer[] i) {
275: for (int x = 0; x < i.length; x++) {
276: i[x] = new Integer(i[x].intValue() + 1);
277: }
278: }
279:
280: private static void modifyFloatWrapper(Float[] f) {
281: for (int i = 0; i < f.length; i++) {
282: f[i] = new Float(f[i].floatValue() + 1);
283: }
284: }
285:
286: private static void modifyDoubleWrapper(Double[] d) {
287: for (int i = 0; i < d.length; i++) {
288: d[i] = new Double(d[i].doubleValue() + 1);
289: }
290: }
291:
292: private static void modifyCharWrapper(Character[] c) {
293: for (int i = 0; i < c.length; i++) {
294: c[i] = new Character((char) (c[i].charValue() + 1));
295: }
296: }
297:
298: private static void modifyByteWrapper(Byte[] b) {
299: for (int i = 0; i < b.length; i++) {
300: b[i] = new Byte((byte) (b[i].byteValue() + 1));
301: }
302: }
303:
304: private static void modifyBooleanWrapper(Boolean[] b) {
305: for (int i = 0; i < b.length; i++) {
306: b[i] = new Boolean(!b[i].booleanValue());
307: }
308: }
309:
310: private static void modifyShort(short[] s) {
311: for (int i = 0; i < s.length; i++) {
312: s[i]++;
313: }
314: }
315:
316: private static void modifyLong(long[] l) {
317: for (int i = 0; i < l.length; i++) {
318: l[i]++;
319: }
320: }
321:
322: private static void modifyInt(int[] i) {
323: for (int x = 0; x < i.length; x++) {
324: i[x]++;
325: }
326: }
327:
328: private static void modifyFloat(float[] f) {
329: for (int i = 0; i < f.length; i++) {
330: f[i]++;
331: }
332: }
333:
334: private static void modifyDouble(double[] d) {
335: for (int i = 0; i < d.length; i++) {
336: d[i]++;
337: }
338: }
339:
340: private static void modifyChar(char[] c) {
341: for (int i = 0; i < c.length; i++) {
342: c[i]++;
343: }
344: }
345:
346: private static void modifyByte(byte[] b) {
347: for (int i = 0; i < b.length; i++) {
348: b[i]++;
349: }
350: }
351:
352: private static void modifyBoolean(boolean[] b) {
353: for (int i = 0; i < b.length; i++) {
354: b[i] = !b[i];
355: }
356: }
357:
358: private static boolean compareData(Object array, Object compare) {
359: int type = intClassType(array.getClass().getComponentType());
360:
361: switch (type) {
362: case BOOLEAN:
363: return Arrays
364: .equals((boolean[]) array, (boolean[]) compare);
365: case BYTE:
366: return Arrays.equals((byte[]) array, (byte[]) compare);
367: case CHAR:
368: return Arrays.equals((char[]) array, (char[]) compare);
369: case DOUBLE:
370: return Arrays.equals((double[]) array, (double[]) compare);
371: case FLOAT:
372: return Arrays.equals((float[]) array, (float[]) compare);
373: case INT:
374: return Arrays.equals((int[]) array, (int[]) compare);
375: case LONG:
376: return Arrays.equals((long[]) array, (long[]) compare);
377: case SHORT:
378: return Arrays.equals((short[]) array, (short[]) compare);
379: default:
380: return Arrays.equals((Object[]) array, (Object[]) compare);
381: }
382:
383: // unreachable
384: }
385:
386: private static Object createRandomArray(Random random, Class type) {
387: int length = 10 + random.nextInt(100);
388: Assert.assertTrue("length = " + length, length > 0);
389:
390: switch (intClassType(type)) {
391: case BOOLEAN:
392: boolean[] b = new boolean[length];
393: for (int i = 0; i < b.length; i++) {
394: b[i] = random.nextBoolean();
395: }
396: return b;
397: case BYTE:
398: return makeBuffer(random, length).array();
399: case CHAR:
400: char[] c = new char[length];
401: makeBuffer(random, length * 2).asCharBuffer().get(c);
402: return c;
403: case DOUBLE:
404: double[] d = new double[length];
405: makeBuffer(random, length * 8).asDoubleBuffer().get(d);
406: return d;
407: case FLOAT:
408: float f[] = new float[length];
409: makeBuffer(random, length * 4).asFloatBuffer().get(f);
410: return f;
411: case INT:
412: int[] i = new int[length];
413: makeBuffer(random, length * 4).asIntBuffer().get(i);
414: return i;
415: case LONG:
416: long[] l = new long[length];
417: makeBuffer(random, length * 8).asLongBuffer().get(l);
418: return l;
419: case SHORT:
420: short[] s = new short[length];
421: makeBuffer(random, length * 2).asShortBuffer().get(s);
422: return s;
423: case BOOLEAN_WRAPPER:
424: return ArrayUtils.toObject((boolean[]) createRandomArray(
425: random, Boolean.TYPE));
426: case BYTE_WRAPPER:
427: return ArrayUtils.toObject((byte[]) createRandomArray(
428: random, Byte.TYPE));
429: case CHAR_WRAPPER:
430: // I don't know why there isn't char[] version of ArrayUtils.toObject()
431: return makeCharacterArray((char[]) createRandomArray(
432: random, Character.TYPE));
433: case DOUBLE_WRAPPER:
434: return ArrayUtils.toObject((double[]) createRandomArray(
435: random, Double.TYPE));
436: case FLOAT_WRAPPER:
437: return ArrayUtils.toObject((float[]) createRandomArray(
438: random, Float.TYPE));
439: case INT_WRAPPER:
440: return ArrayUtils.toObject((int[]) createRandomArray(
441: random, Integer.TYPE));
442: case LONG_WRAPPER:
443: return ArrayUtils.toObject((long[]) createRandomArray(
444: random, Long.TYPE));
445: case SHORT_WRAPPER:
446: return ArrayUtils.toObject((short[]) createRandomArray(
447: random, Short.TYPE));
448: default:
449: throw new RuntimeException("bad type: " + type);
450: }
451: }
452:
453: private static Character[] makeCharacterArray(char[] c) {
454: Character rv[] = new Character[c.length];
455: for (int i = 0; i < rv.length; i++) {
456: rv[i] = new Character(c[i]);
457: }
458: return rv;
459: }
460:
461: private static int intClassType(Class type) {
462: if (classToInt.containsKey(type)) {
463: return classToInt.get(type);
464: }
465: throw new RuntimeException("No mapping for " + type);
466: }
467:
468: private static ByteBuffer makeBuffer(Random random, int length) {
469: byte[] data = new byte[length];
470: random.nextBytes(data);
471: return ByteBuffer.wrap(data);
472: }
473:
474: private static class DataRoot {
475: private final Map seeds = new HashMap();
476: private final List arrays = new ArrayList();
477: private int index = 0;
478:
479: DataRoot() {
480:
481: boolean[] booleanData = (boolean[]) makeArray(secure
482: .nextLong(), Boolean.TYPE);
483: byte[] byteData = (byte[]) makeArray(secure.nextLong(),
484: Byte.TYPE);
485: char[] charData = (char[]) makeArray(secure.nextLong(),
486: Character.TYPE);
487: double[] doubleData = (double[]) makeArray(secure
488: .nextLong(), Double.TYPE);
489: float[] floatData = (float[]) makeArray(secure.nextLong(),
490: Float.TYPE);
491: int[] intData = (int[]) makeArray(secure.nextLong(),
492: Integer.TYPE);
493: long[] longData = (long[]) makeArray(secure.nextLong(),
494: Long.TYPE);
495: short[] shortData = (short[]) makeArray(secure.nextLong(),
496: Short.TYPE);
497:
498: Boolean[] booleanWrapperData = (Boolean[]) makeArray(secure
499: .nextLong(), Boolean.class);
500: Byte[] byteWrapperData = (Byte[]) makeArray(secure
501: .nextLong(), Byte.class);
502: Character[] charWrapperData = (Character[]) makeArray(
503: secure.nextLong(), Character.class);
504: Double[] doubleWrapperData = (Double[]) makeArray(secure
505: .nextLong(), Double.class);
506: Float[] floatWrapperData = (Float[]) makeArray(secure
507: .nextLong(), Float.class);
508: Integer[] intWrapperData = (Integer[]) makeArray(secure
509: .nextLong(), Integer.class);
510: Long[] longWrapperData = (Long[]) makeArray(secure
511: .nextLong(), Long.class);
512: Short[] shortWrapperData = (Short[]) makeArray(secure
513: .nextLong(), Short.class);
514:
515: arrays.add(booleanData);
516: arrays.add(byteData);
517: arrays.add(charData);
518: arrays.add(doubleData);
519: arrays.add(floatData);
520: arrays.add(intData);
521: arrays.add(longData);
522: arrays.add(shortData);
523:
524: arrays.add(booleanWrapperData);
525: arrays.add(byteWrapperData);
526: arrays.add(charWrapperData);
527: arrays.add(doubleWrapperData);
528: arrays.add(floatWrapperData);
529: arrays.add(intWrapperData);
530: arrays.add(longWrapperData);
531: arrays.add(shortWrapperData);
532: }
533:
534: public Iterator getArrayIterator() {
535: return Collections.unmodifiableList(arrays).iterator();
536: }
537:
538: int getIndex() {
539: return this .index;
540: }
541:
542: int getNumArrays() {
543: return this .arrays.size();
544: }
545:
546: Object getArray() {
547: synchronized (arrays) {
548: return arrays.get(index++);
549: }
550: }
551:
552: Object getCompareData(Class type) {
553: long seed = ((Long) seeds.get(type.getName())).longValue();
554: return createRandomArray(new Random(seed), type);
555: }
556:
557: private Object makeArray(long seed, Class type) {
558: System.err.println("Seed for type " + type + "=" + seed);
559: seeds.put(type.getName(), new Long(seed));
560: return createRandomArray(new Random(seed), type);
561: }
562: }
563:
564: }
|