1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 2002,2008 Oracle.  All rights reserved.
5 *
6 * $Id: BindingSpeedTest.java,v 12.9 2008/02/07 17:12:30 mark Exp $
7 */
8
9package com.sleepycat.bind.test;
10
11import java.io.Externalizable;
12import java.io.IOException;
13import java.io.ObjectInput;
14import java.io.ObjectInputStream;
15import java.io.ObjectOutput;
16import java.io.ObjectOutputStream;
17import java.io.OutputStreamWriter;
18import java.io.Serializable;
19import java.io.Writer;
20import java.lang.reflect.Field;
21import java.lang.reflect.Method;
22
23import javax.xml.parsers.SAXParserFactory;
24
25import junit.framework.Test;
26import junit.framework.TestCase;
27import junit.framework.TestSuite;
28
29import org.xml.sax.InputSource;
30import org.xml.sax.XMLReader;
31
32import com.sleepycat.bind.serial.SerialInput;
33import com.sleepycat.bind.serial.SerialOutput;
34import com.sleepycat.bind.serial.test.TestClassCatalog;
35import com.sleepycat.bind.tuple.TupleInput;
36import com.sleepycat.bind.tuple.TupleOutput;
37import com.sleepycat.util.FastInputStream;
38import com.sleepycat.util.FastOutputStream;
39import com.sleepycat.util.test.SharedTestUtils;
40
41/**
42 * @author Mark Hayes
43 */
44public class BindingSpeedTest extends TestCase {
45
46    static final String JAVA_UNSHARED = "java-unshared".intern();
47    static final String JAVA_SHARED = "java-shared".intern();
48    static final String JAVA_EXTERNALIZABLE = "java-externalizable".intern();
49    static final String XML_SAX = "xml-sax".intern();
50    static final String TUPLE = "tuple".intern();
51    static final String REFLECT_METHOD = "reflectMethod".intern();
52    static final String REFLECT_FIELD = "reflectField".intern();
53
54    static final int RUN_COUNT = 1000;
55    static final boolean VERBOSE = false;
56
57    public static void main(String[] args)
58        throws Exception {
59
60        junit.framework.TestResult tr =
61            junit.textui.TestRunner.run(suite());
62        if (tr.errorCount() > 0 ||
63            tr.failureCount() > 0) {
64            System.exit(1);
65        } else {
66            System.exit(0);
67        }
68    }
69
70    public static Test suite() {
71
72        TestSuite suite = new TestSuite();
73        suite.addTest(new BindingSpeedTest(JAVA_UNSHARED));
74        suite.addTest(new BindingSpeedTest(JAVA_SHARED));
75        suite.addTest(new BindingSpeedTest(JAVA_EXTERNALIZABLE));
76        suite.addTest(new BindingSpeedTest(XML_SAX));
77        suite.addTest(new BindingSpeedTest(TUPLE));
78        suite.addTest(new BindingSpeedTest(REFLECT_METHOD));
79        suite.addTest(new BindingSpeedTest(REFLECT_FIELD));
80        return suite;
81    }
82
83    private String command;
84    private FastOutputStream fo;
85    private TupleOutput to;
86    private TestClassCatalog jtc;
87    private byte[] buf;
88    private XMLReader parser;
89    private Method[] getters;
90    private Method[] setters;
91    private Field[] fields;
92
93    public BindingSpeedTest(String name) {
94
95        super("BindingSpeedTest." + name);
96        command = name;
97    }
98
99    public void runTest()
100        throws Exception {
101
102        SharedTestUtils.printTestName(getName());
103
104        boolean isTuple = false;
105        boolean isReflectMethod = false;
106        boolean isReflectField = false;
107        boolean isXmlSax = false;
108        boolean isSerial = false;
109        boolean isShared = false;
110        boolean isExternalizable = false;
111
112        if (command == TUPLE) {
113            isTuple = true;
114        } else if (command == REFLECT_METHOD) {
115            isReflectMethod = true;
116        } else if (command == REFLECT_FIELD) {
117            isReflectField = true;
118        } else if (command == XML_SAX) {
119            isXmlSax = true;
120        } else if (command == JAVA_UNSHARED) {
121            isSerial = true;
122        } else if (command == JAVA_SHARED) {
123            isSerial = true;
124            isShared = true;
125        } else if (command == JAVA_EXTERNALIZABLE) {
126            isSerial = true;
127            isShared = true;
128            isExternalizable = true;
129        } else {
130            throw new Exception("invalid command: " + command);
131        }
132
133        // Do initialization
134
135        if (isTuple) {
136            initTuple();
137        } else if (isReflectMethod) {
138            initReflectMethod();
139        } else if (isReflectField) {
140            initReflectField();
141        } else if (isXmlSax) {
142            initXmlSax();
143        } else if (isSerial) {
144            if (isShared) {
145                initSerialShared();
146            } else {
147                initSerialUnshared();
148            }
149        }
150
151        // Prime the Java compiler
152
153        int size = 0;
154        for (int i = 0; i < RUN_COUNT; i += 1) {
155
156            if (isTuple) {
157                size = runTuple();
158            } else if (isReflectMethod) {
159                size = runReflectMethod();
160            } else if (isReflectField) {
161                size = runReflectField();
162            } else if (isXmlSax) {
163                size = runXmlSax();
164            } else if (isSerial) {
165                if (isShared) {
166                    if (isExternalizable) {
167                        size = runSerialExternalizable();
168                    } else {
169                        size = runSerialShared();
170                    }
171                } else {
172                    size = runSerialUnshared();
173                }
174            }
175        }
176
177        // Then run the timing tests
178
179        long startTime = System.currentTimeMillis();
180
181        for (int i = 0; i < RUN_COUNT; i += 1) {
182            if (isTuple) {
183                size = runTuple();
184            } else if (isReflectMethod) {
185                size = runReflectMethod();
186            } else if (isReflectField) {
187                size = runReflectField();
188            } else if (isXmlSax) {
189                size = runXmlSax();
190            } else if (isSerial) {
191                if (isShared) {
192                    if (isExternalizable) {
193                        size = runSerialExternalizable();
194                    } else {
195                        size = runSerialShared();
196                    }
197                } else {
198                    size = runSerialUnshared();
199                }
200            }
201        }
202
203        long stopTime = System.currentTimeMillis();
204
205	assertTrue("data size too big", size < 250);
206
207        if (VERBOSE) {
208            System.out.println(command);
209            System.out.println("data size: " + size);
210            System.out.println("run time:  " +
211                ((stopTime - startTime) / (double) RUN_COUNT));
212        }
213    }
214
215    public void tearDown() {
216
217        /* Ensure that GC can cleanup. */
218        command = null;
219        fo = null;
220        to = null;
221        jtc = null;
222        buf = null;
223        parser = null;
224    }
225
226    void initSerialUnshared()
227        throws Exception {
228
229        fo = new FastOutputStream();
230    }
231
232    int runSerialUnshared()
233        throws Exception {
234
235        fo.reset();
236        ObjectOutputStream oos = new ObjectOutputStream(fo);
237        oos.writeObject(new Data());
238        byte[] bytes = fo.toByteArray();
239        FastInputStream fi = new FastInputStream(bytes);
240        ObjectInputStream ois = new ObjectInputStream(fi);
241        ois.readObject();
242        return bytes.length;
243    }
244
245    void initSerialShared()
246        throws Exception {
247
248        jtc = new TestClassCatalog();
249        fo = new FastOutputStream();
250    }
251
252    int runSerialShared()
253        throws Exception {
254
255        fo.reset();
256        SerialOutput oos = new SerialOutput(fo, jtc);
257        oos.writeObject(new Data());
258        byte[] bytes = fo.toByteArray();
259        FastInputStream fi = new FastInputStream(bytes);
260        SerialInput ois = new SerialInput(fi, jtc);
261        ois.readObject();
262        return (bytes.length - SerialOutput.getStreamHeader().length);
263    }
264
265    int runSerialExternalizable()
266        throws Exception {
267
268        fo.reset();
269        SerialOutput oos = new SerialOutput(fo, jtc);
270        oos.writeObject(new Data2());
271        byte[] bytes = fo.toByteArray();
272        FastInputStream fi = new FastInputStream(bytes);
273        SerialInput ois = new SerialInput(fi, jtc);
274        ois.readObject();
275        return (bytes.length - SerialOutput.getStreamHeader().length);
276    }
277
278    void initTuple()
279        throws Exception {
280
281        buf = new byte[500];
282        to = new TupleOutput(buf);
283    }
284
285    int runTuple()
286        throws Exception {
287
288        to.reset();
289        new Data().writeTuple(to);
290
291        TupleInput ti = new TupleInput(
292                          to.getBufferBytes(), to.getBufferOffset(),
293                          to.getBufferLength());
294        new Data().readTuple(ti);
295
296        return to.getBufferLength();
297    }
298
299    void initReflectMethod()
300        throws Exception {
301
302        initTuple();
303
304        Class cls = Data.class;
305
306        getters = new Method[5];
307        getters[0] = cls.getMethod("getField1", new Class[0]);
308        getters[1] = cls.getMethod("getField2", new Class[0]);
309        getters[2] = cls.getMethod("getField3", new Class[0]);
310        getters[3] = cls.getMethod("getField4", new Class[0]);
311        getters[4] = cls.getMethod("getField5", new Class[0]);
312
313        setters = new Method[5];
314        setters[0] = cls.getMethod("setField1", new Class[] {String.class});
315        setters[1] = cls.getMethod("setField2", new Class[] {String.class});
316        setters[2] = cls.getMethod("setField3", new Class[] {Integer.TYPE});
317        setters[3] = cls.getMethod("setField4", new Class[] {Integer.TYPE});
318        setters[4] = cls.getMethod("setField5", new Class[] {String.class});
319    }
320
321    int runReflectMethod()
322        throws Exception {
323
324        to.reset();
325        Data data = new Data();
326        to.writeString((String) getters[0].invoke(data, (Object[]) null));
327        to.writeString((String) getters[1].invoke(data, (Object[]) null));
328        to.writeInt(((Integer) getters[2].invoke(data, (Object[]) null)).intValue());
329        to.writeInt(((Integer) getters[3].invoke(data, (Object[]) null)).intValue());
330        to.writeString((String) getters[4].invoke(data, (Object[]) null));
331
332        TupleInput ti = new TupleInput(
333                          to.getBufferBytes(), to.getBufferOffset(),
334                          to.getBufferLength());
335        data = new Data();
336        setters[0].invoke(data, new Object[] {ti.readString()});
337        setters[1].invoke(data, new Object[] {ti.readString()});
338        setters[2].invoke(data, new Object[] {new Integer(ti.readInt())});
339        setters[3].invoke(data, new Object[] {new Integer(ti.readInt())});
340        setters[4].invoke(data, new Object[] {ti.readString()});
341
342        return to.getBufferLength();
343    }
344
345    void initReflectField()
346        throws Exception {
347
348        initTuple();
349
350        Class cls = Data.class;
351
352        fields = new Field[5];
353        fields[0] = cls.getField("field1");
354        fields[1] = cls.getField("field2");
355        fields[2] = cls.getField("field3");
356        fields[3] = cls.getField("field4");
357        fields[4] = cls.getField("field5");
358    }
359
360    int runReflectField()
361        throws Exception {
362
363        to.reset();
364        Data data = new Data();
365        to.writeString((String) fields[0].get(data));
366        to.writeString((String) fields[1].get(data));
367        to.writeInt(((Integer) fields[2].get(data)).intValue());
368        to.writeInt(((Integer) fields[3].get(data)).intValue());
369        to.writeString((String) fields[4].get(data));
370
371        TupleInput ti = new TupleInput(
372                          to.getBufferBytes(), to.getBufferOffset(),
373                          to.getBufferLength());
374        data = new Data();
375        fields[0].set(data, ti.readString());
376        fields[1].set(data, ti.readString());
377        fields[2].set(data, new Integer(ti.readInt()));
378        fields[3].set(data, new Integer(ti.readInt()));
379        fields[4].set(data, ti.readString());
380
381        return to.getBufferLength();
382    }
383
384    void initXmlSax()
385        throws Exception {
386
387        buf = new byte[500];
388        fo = new FastOutputStream();
389        SAXParserFactory saxFactory = SAXParserFactory.newInstance();
390        saxFactory.setNamespaceAware(true);
391        parser = saxFactory.newSAXParser().getXMLReader();
392    }
393
394    int runXmlSax()
395        throws Exception {
396
397        fo.reset();
398        OutputStreamWriter writer = new OutputStreamWriter(fo);
399        new Data().writeXmlText(writer);
400
401        byte[] bytes = fo.toByteArray();
402        FastInputStream fi = new FastInputStream(bytes);
403        InputSource input = new InputSource(fi);
404        parser.parse(input);
405
406        //InputStreamReader reader = new InputStreamReader(fi);
407        //new Data().readXmlText(??);
408
409        return bytes.length;
410    }
411
412    static class Data2 extends Data implements Externalizable {
413
414        public Data2() {}
415
416        public void readExternal(ObjectInput in)
417            throws IOException, ClassNotFoundException {
418
419            field1 = in.readUTF();
420            field2 = in.readUTF();
421            field3 = in.readInt();
422            field4 = in.readInt();
423            field5 = in.readUTF();
424        }
425
426        public void writeExternal(ObjectOutput out)
427            throws IOException {
428
429            out.writeUTF(field1);
430            out.writeUTF(field2);
431            out.writeInt(field3);
432            out.writeInt(field4);
433            out.writeUTF(field5);
434        }
435    }
436
437    static class Data implements Serializable {
438
439        public String field1 = "field1";
440        public String field2 = "field2";
441        public int field3 = 333;
442        public int field4 = 444;
443        public String field5 = "field5";
444
445        public String getField1() { return field1; }
446        public String getField2() { return field2; }
447        public int getField3() { return field3; }
448        public int getField4() { return field4; }
449        public String getField5() { return field5; }
450
451        public void setField1(String v) { field1 = v; }
452        public void setField2(String v) { field2 = v; }
453        public void setField3(int v) { field3 = v; }
454        public void setField4(int v) { field4 = v; }
455        public void setField5(String v) { field5 = v; }
456
457        void readTuple(TupleInput _input) {
458
459            field1 = _input.readString();
460            field2 = _input.readString();
461            field3 = _input.readInt();
462            field4 = _input.readInt();
463            field5 = _input.readString();
464        }
465
466        void writeTuple(TupleOutput _output) {
467
468            _output.writeString(field1);
469            _output.writeString(field2);
470            _output.writeInt(field3);
471            _output.writeInt(field4);
472            _output.writeString(field5);
473        }
474
475        void writeXmlText(Writer writer) throws IOException {
476
477            writer.write("<Data><Field1>");
478            writer.write(field1);
479            writer.write("</Field1><Field2>");
480            writer.write(field2);
481            writer.write("</Field2><Field3>");
482            writer.write(String.valueOf(field3));
483            writer.write("</Field3><Field4>");
484            writer.write(String.valueOf(field4));
485            writer.write("</Field4><Field5>");
486            writer.write(field5);
487            writer.write("</Field5></Data>");
488            writer.flush();
489        }
490    }
491}
492