001/**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.activemq.openwire.v11;
018
019import java.io.DataInput;
020import java.io.DataOutput;
021import java.io.IOException;
022import java.lang.reflect.Constructor;
023import org.apache.activemq.command.DataStructure;
024import org.apache.activemq.openwire.BooleanStream;
025import org.apache.activemq.openwire.DataStreamMarshaller;
026import org.apache.activemq.openwire.OpenWireFormat;
027import org.apache.activemq.openwire.OpenWireUtil;
028import org.apache.activemq.util.ByteSequence;
029
030public abstract class BaseDataStreamMarshaller implements DataStreamMarshaller {
031
032    public static final Constructor STACK_TRACE_ELEMENT_CONSTRUCTOR;
033
034    static {
035        Constructor constructor = null;
036        try {
037            constructor = StackTraceElement.class.getConstructor(new Class[] {String.class, String.class,
038                                                                              String.class, int.class});
039        } catch (Throwable e) {
040        }
041        STACK_TRACE_ELEMENT_CONSTRUCTOR = constructor;
042    }
043
044    public abstract byte getDataStructureType();
045
046    public abstract DataStructure createObject();
047
048    public int tightMarshal1(OpenWireFormat wireFormat, Object o, BooleanStream bs) throws IOException {
049        return 0;
050    }
051
052    public void tightMarshal2(OpenWireFormat wireFormat, Object o, DataOutput dataOut, BooleanStream bs)
053        throws IOException {
054    }
055
056    public void tightUnmarshal(OpenWireFormat wireFormat, Object o, DataInput dataIn, BooleanStream bs)
057        throws IOException {
058    }
059
060    public int tightMarshalLong1(OpenWireFormat wireFormat, long o, BooleanStream bs) throws IOException {
061        if (o == 0) {
062            bs.writeBoolean(false);
063            bs.writeBoolean(false);
064            return 0;
065        } else if ((o & 0xFFFFFFFFFFFF0000L) == 0) {
066            bs.writeBoolean(false);
067            bs.writeBoolean(true);
068            return 2;
069        } else if ((o & 0xFFFFFFFF00000000L) == 0) {
070            bs.writeBoolean(true);
071            bs.writeBoolean(false);
072            return 4;
073        } else {
074            bs.writeBoolean(true);
075            bs.writeBoolean(true);
076            return 8;
077        }
078    }
079
080    public void tightMarshalLong2(OpenWireFormat wireFormat, long o, DataOutput dataOut, BooleanStream bs)
081        throws IOException {
082        if (bs.readBoolean()) {
083            if (bs.readBoolean()) {
084                dataOut.writeLong(o);
085            } else {
086                dataOut.writeInt((int)o);
087            }
088        } else {
089            if (bs.readBoolean()) {
090                dataOut.writeShort((int)o);
091            }
092        }
093    }
094
095    public long tightUnmarshalLong(OpenWireFormat wireFormat, DataInput dataIn, BooleanStream bs)
096        throws IOException {
097        if (bs.readBoolean()) {
098            if (bs.readBoolean()) {
099                return dataIn.readLong();
100            } else {
101                return toLong(dataIn.readInt());
102            }
103        } else {
104            if (bs.readBoolean()) {
105                return toLong(dataIn.readShort());
106            } else {
107                return 0;
108            }
109        }
110    }
111
112    protected long toLong(short value) {
113        // lets handle negative values
114        long answer = value;
115        return answer & 0xffffL;
116    }
117
118    protected long toLong(int value) {
119        // lets handle negative values
120        long answer = value;
121        return answer & 0xffffffffL;
122    }
123
124    protected DataStructure tightUnmarsalNestedObject(OpenWireFormat wireFormat, DataInput dataIn,
125                                                      BooleanStream bs) throws IOException {
126        return wireFormat.tightUnmarshalNestedObject(dataIn, bs);
127    }
128
129    protected int tightMarshalNestedObject1(OpenWireFormat wireFormat, DataStructure o, BooleanStream bs)
130        throws IOException {
131        return wireFormat.tightMarshalNestedObject1(o, bs);
132    }
133
134    protected void tightMarshalNestedObject2(OpenWireFormat wireFormat, DataStructure o, DataOutput dataOut,
135                                             BooleanStream bs) throws IOException {
136        wireFormat.tightMarshalNestedObject2(o, dataOut, bs);
137    }
138
139    protected DataStructure tightUnmarsalCachedObject(OpenWireFormat wireFormat, DataInput dataIn,
140                                                      BooleanStream bs) throws IOException {
141        if (wireFormat.isCacheEnabled()) {
142            if (bs.readBoolean()) {
143                short index = dataIn.readShort();
144                DataStructure object = wireFormat.tightUnmarshalNestedObject(dataIn, bs);
145                wireFormat.setInUnmarshallCache(index, object);
146                return object;
147            } else {
148                short index = dataIn.readShort();
149                return wireFormat.getFromUnmarshallCache(index);
150            }
151        } else {
152            return wireFormat.tightUnmarshalNestedObject(dataIn, bs);
153        }
154    }
155
156    protected int tightMarshalCachedObject1(OpenWireFormat wireFormat, DataStructure o, BooleanStream bs)
157        throws IOException {
158        if (wireFormat.isCacheEnabled()) {
159            Short index = wireFormat.getMarshallCacheIndex(o);
160            bs.writeBoolean(index == null);
161            if (index == null) {
162                int rc = wireFormat.tightMarshalNestedObject1(o, bs);
163                wireFormat.addToMarshallCache(o);
164                return 2 + rc;
165            } else {
166                return 2;
167            }
168        } else {
169            return wireFormat.tightMarshalNestedObject1(o, bs);
170        }
171    }
172
173    protected void tightMarshalCachedObject2(OpenWireFormat wireFormat, DataStructure o, DataOutput dataOut,
174                                             BooleanStream bs) throws IOException {
175        if (wireFormat.isCacheEnabled()) {
176            Short index = wireFormat.getMarshallCacheIndex(o);
177            if (bs.readBoolean()) {
178                dataOut.writeShort(index.shortValue());
179                wireFormat.tightMarshalNestedObject2(o, dataOut, bs);
180            } else {
181                dataOut.writeShort(index.shortValue());
182            }
183        } else {
184            wireFormat.tightMarshalNestedObject2(o, dataOut, bs);
185        }
186    }
187
188    protected Throwable tightUnmarsalThrowable(OpenWireFormat wireFormat, DataInput dataIn, BooleanStream bs)
189        throws IOException {
190        if (bs.readBoolean()) {
191            String clazz = tightUnmarshalString(dataIn, bs);
192            String message = tightUnmarshalString(dataIn, bs);
193            Throwable o = createThrowable(clazz, message);
194            if (wireFormat.isStackTraceEnabled()) {
195                if (STACK_TRACE_ELEMENT_CONSTRUCTOR != null) {
196                    StackTraceElement ss[] = new StackTraceElement[dataIn.readShort()];
197                    for (int i = 0; i < ss.length; i++) {
198                        try {
199                            ss[i] = (StackTraceElement)STACK_TRACE_ELEMENT_CONSTRUCTOR
200                                .newInstance(new Object[] {tightUnmarshalString(dataIn, bs),
201                                                           tightUnmarshalString(dataIn, bs),
202                                                           tightUnmarshalString(dataIn, bs),
203                                                           Integer.valueOf(dataIn.readInt())});
204                        } catch (IOException e) {
205                            throw e;
206                        } catch (Throwable e) {
207                        }
208                    }
209                    o.setStackTrace(ss);
210                } else {
211                    short size = dataIn.readShort();
212                    for (int i = 0; i < size; i++) {
213                        tightUnmarshalString(dataIn, bs);
214                        tightUnmarshalString(dataIn, bs);
215                        tightUnmarshalString(dataIn, bs);
216                        dataIn.readInt();
217                    }
218                }
219                o.initCause(tightUnmarsalThrowable(wireFormat, dataIn, bs));
220
221            }
222            return o;
223        } else {
224            return null;
225        }
226    }
227
228    private Throwable createThrowable(String className, String message) {
229        try {
230            Class clazz = Class.forName(className, false, BaseDataStreamMarshaller.class.getClassLoader());
231            OpenWireUtil.validateIsThrowable(clazz);
232            Constructor constructor = clazz.getConstructor(new Class[] {String.class});
233            return (Throwable)constructor.newInstance(new Object[] {message});
234        } catch (IllegalArgumentException e) {
235            return e;
236        } catch (Throwable e) {
237            return new Throwable(className + ": " + message);
238        }
239    }
240
241    protected int tightMarshalThrowable1(OpenWireFormat wireFormat, Throwable o, BooleanStream bs)
242        throws IOException {
243        if (o == null) {
244            bs.writeBoolean(false);
245            return 0;
246        } else {
247            int rc = 0;
248            bs.writeBoolean(true);
249            rc += tightMarshalString1(o.getClass().getName(), bs);
250            rc += tightMarshalString1(o.getMessage(), bs);
251            if (wireFormat.isStackTraceEnabled()) {
252                rc += 2;
253                StackTraceElement[] stackTrace = o.getStackTrace();
254                for (int i = 0; i < stackTrace.length; i++) {
255                    StackTraceElement element = stackTrace[i];
256                    rc += tightMarshalString1(element.getClassName(), bs);
257                    rc += tightMarshalString1(element.getMethodName(), bs);
258                    rc += tightMarshalString1(element.getFileName(), bs);
259                    rc += 4;
260                }
261                rc += tightMarshalThrowable1(wireFormat, o.getCause(), bs);
262            }
263            return rc;
264        }
265    }
266
267    protected void tightMarshalThrowable2(OpenWireFormat wireFormat, Throwable o, DataOutput dataOut,
268                                          BooleanStream bs) throws IOException {
269        if (bs.readBoolean()) {
270            tightMarshalString2(o.getClass().getName(), dataOut, bs);
271            tightMarshalString2(o.getMessage(), dataOut, bs);
272            if (wireFormat.isStackTraceEnabled()) {
273                StackTraceElement[] stackTrace = o.getStackTrace();
274                dataOut.writeShort(stackTrace.length);
275                for (int i = 0; i < stackTrace.length; i++) {
276                    StackTraceElement element = stackTrace[i];
277                    tightMarshalString2(element.getClassName(), dataOut, bs);
278                    tightMarshalString2(element.getMethodName(), dataOut, bs);
279                    tightMarshalString2(element.getFileName(), dataOut, bs);
280                    dataOut.writeInt(element.getLineNumber());
281                }
282                tightMarshalThrowable2(wireFormat, o.getCause(), dataOut, bs);
283            }
284        }
285    }
286
287    @SuppressWarnings("deprecation")
288    protected String tightUnmarshalString(DataInput dataIn, BooleanStream bs) throws IOException {
289        if (bs.readBoolean()) {
290            if (bs.readBoolean()) {
291                int size = dataIn.readShort();
292                byte data[] = new byte[size];
293                dataIn.readFully(data);
294                // Yes deprecated, but we know what we are doing.
295                // This allows us to create a String from a ASCII byte array. (no UTF-8 decoding)
296                return new String(data, 0);
297            } else {
298                return dataIn.readUTF();
299            }
300        } else {
301            return null;
302        }
303    }
304
305    protected int tightMarshalString1(String value, BooleanStream bs) throws IOException {
306        bs.writeBoolean(value != null);
307        if (value != null) {
308
309            int strlen = value.length();
310            int utflen = 0;
311            char[] charr = new char[strlen];
312            int c = 0;
313            boolean isOnlyAscii = true;
314
315            value.getChars(0, strlen, charr, 0);
316
317            for (int i = 0; i < strlen; i++) {
318                c = charr[i];
319                if ((c >= 0x0001) && (c <= 0x007F)) {
320                    utflen++;
321                } else if (c > 0x07FF) {
322                    utflen += 3;
323                    isOnlyAscii = false;
324                } else {
325                    isOnlyAscii = false;
326                    utflen += 2;
327                }
328            }
329
330            if (utflen >= Short.MAX_VALUE) {
331                throw new IOException("Encountered a String value that is too long to encode.");
332            }
333            bs.writeBoolean(isOnlyAscii);
334            return utflen + 2;
335
336        } else {
337            return 0;
338        }
339    }
340
341    protected void tightMarshalString2(String value, DataOutput dataOut, BooleanStream bs) throws IOException {
342        if (bs.readBoolean()) {
343            // If we verified it only holds ascii values
344            if (bs.readBoolean()) {
345                dataOut.writeShort(value.length());
346                dataOut.writeBytes(value);
347            } else {
348                dataOut.writeUTF(value);
349            }
350        }
351    }
352
353    protected int tightMarshalObjectArray1(OpenWireFormat wireFormat, DataStructure[] objects,
354                                           BooleanStream bs) throws IOException {
355        if (objects != null) {
356            int rc = 0;
357            bs.writeBoolean(true);
358            rc += 2;
359            for (int i = 0; i < objects.length; i++) {
360                rc += tightMarshalNestedObject1(wireFormat, objects[i], bs);
361            }
362            return rc;
363        } else {
364            bs.writeBoolean(false);
365            return 0;
366        }
367    }
368
369    protected void tightMarshalObjectArray2(OpenWireFormat wireFormat, DataStructure[] objects,
370                                            DataOutput dataOut, BooleanStream bs) throws IOException {
371        if (bs.readBoolean()) {
372            dataOut.writeShort(objects.length);
373            for (int i = 0; i < objects.length; i++) {
374                tightMarshalNestedObject2(wireFormat, objects[i], dataOut, bs);
375            }
376        }
377    }
378
379    protected int tightMarshalConstByteArray1(byte[] data, BooleanStream bs, int i) throws IOException {
380        return i;
381    }
382
383    protected void tightMarshalConstByteArray2(byte[] data, DataOutput dataOut, BooleanStream bs, int i)
384        throws IOException {
385        dataOut.write(data, 0, i);
386    }
387
388    protected byte[] tightUnmarshalConstByteArray(DataInput dataIn, BooleanStream bs, int i)
389        throws IOException {
390        byte data[] = new byte[i];
391        dataIn.readFully(data);
392        return data;
393    }
394
395    protected int tightMarshalByteArray1(byte[] data, BooleanStream bs) throws IOException {
396        bs.writeBoolean(data != null);
397        if (data != null) {
398            return data.length + 4;
399        } else {
400            return 0;
401        }
402    }
403
404    protected void tightMarshalByteArray2(byte[] data, DataOutput dataOut, BooleanStream bs)
405        throws IOException {
406        if (bs.readBoolean()) {
407            dataOut.writeInt(data.length);
408            dataOut.write(data);
409        }
410    }
411
412    protected byte[] tightUnmarshalByteArray(OpenWireFormat wireFormat, DataInput dataIn, BooleanStream bs) throws IOException {
413        byte rc[] = null;
414        if (bs.readBoolean()) {
415            int size = dataIn.readInt();
416            OpenWireUtil.validateBufferSize(wireFormat, size);
417            rc = new byte[size];
418            dataIn.readFully(rc);
419        }
420        return rc;
421    }
422
423    protected int tightMarshalByteSequence1(ByteSequence data, BooleanStream bs) throws IOException {
424        bs.writeBoolean(data != null);
425        if (data != null) {
426            return data.getLength() + 4;
427        } else {
428            return 0;
429        }
430    }
431
432    protected void tightMarshalByteSequence2(ByteSequence data, DataOutput dataOut, BooleanStream bs)
433        throws IOException {
434        if (bs.readBoolean()) {
435            dataOut.writeInt(data.getLength());
436            dataOut.write(data.getData(), data.getOffset(), data.getLength());
437        }
438    }
439
440    protected ByteSequence tightUnmarshalByteSequence(OpenWireFormat wireFormat, DataInput dataIn, BooleanStream bs) throws IOException {
441        ByteSequence rc = null;
442        if (bs.readBoolean()) {
443            int size = dataIn.readInt();
444            OpenWireUtil.validateBufferSize(wireFormat, size);
445            byte[] t = new byte[size];
446            dataIn.readFully(t);
447            return new ByteSequence(t, 0, size);
448        }
449        return rc;
450    }
451
452    //
453    // The loose marshaling logic
454    //
455
456    public void looseMarshal(OpenWireFormat wireFormat, Object o, DataOutput dataOut) throws IOException {
457    }
458
459    public void looseUnmarshal(OpenWireFormat wireFormat, Object o, DataInput dataIn) throws IOException {
460    }
461
462    public void looseMarshalLong(OpenWireFormat wireFormat, long o, DataOutput dataOut) throws IOException {
463        dataOut.writeLong(o);
464    }
465
466    public long looseUnmarshalLong(OpenWireFormat wireFormat, DataInput dataIn) throws IOException {
467        return dataIn.readLong();
468    }
469
470    protected DataStructure looseUnmarsalNestedObject(OpenWireFormat wireFormat, DataInput dataIn)
471        throws IOException {
472        return wireFormat.looseUnmarshalNestedObject(dataIn);
473    }
474
475    protected void looseMarshalNestedObject(OpenWireFormat wireFormat, DataStructure o, DataOutput dataOut)
476        throws IOException {
477        wireFormat.looseMarshalNestedObject(o, dataOut);
478    }
479
480    protected DataStructure looseUnmarsalCachedObject(OpenWireFormat wireFormat, DataInput dataIn)
481        throws IOException {
482        if (wireFormat.isCacheEnabled()) {
483            if (dataIn.readBoolean()) {
484                short index = dataIn.readShort();
485                DataStructure object = wireFormat.looseUnmarshalNestedObject(dataIn);
486                wireFormat.setInUnmarshallCache(index, object);
487                return object;
488            } else {
489                short index = dataIn.readShort();
490                return wireFormat.getFromUnmarshallCache(index);
491            }
492        } else {
493            return wireFormat.looseUnmarshalNestedObject(dataIn);
494        }
495    }
496
497    protected void looseMarshalCachedObject(OpenWireFormat wireFormat, DataStructure o, DataOutput dataOut)
498        throws IOException {
499        if (wireFormat.isCacheEnabled()) {
500            Short index = wireFormat.getMarshallCacheIndex(o);
501            dataOut.writeBoolean(index == null);
502            if (index == null) {
503                index = wireFormat.addToMarshallCache(o);
504                dataOut.writeShort(index.shortValue());
505                wireFormat.looseMarshalNestedObject(o, dataOut);
506            } else {
507                dataOut.writeShort(index.shortValue());
508            }
509        } else {
510            wireFormat.looseMarshalNestedObject(o, dataOut);
511        }
512    }
513
514    protected Throwable looseUnmarsalThrowable(OpenWireFormat wireFormat, DataInput dataIn)
515        throws IOException {
516        if (dataIn.readBoolean()) {
517            String clazz = looseUnmarshalString(dataIn);
518            String message = looseUnmarshalString(dataIn);
519            Throwable o = createThrowable(clazz, message);
520            if (wireFormat.isStackTraceEnabled()) {
521                if (STACK_TRACE_ELEMENT_CONSTRUCTOR != null) {
522                    StackTraceElement ss[] = new StackTraceElement[dataIn.readShort()];
523                    for (int i = 0; i < ss.length; i++) {
524                        try {
525                            ss[i] = (StackTraceElement)STACK_TRACE_ELEMENT_CONSTRUCTOR
526                                .newInstance(new Object[] {looseUnmarshalString(dataIn),
527                                                           looseUnmarshalString(dataIn),
528                                                           looseUnmarshalString(dataIn),
529                                                           Integer.valueOf(dataIn.readInt())});
530                        } catch (IOException e) {
531                            throw e;
532                        } catch (Throwable e) {
533                        }
534                    }
535                    o.setStackTrace(ss);
536                } else {
537                    short size = dataIn.readShort();
538                    for (int i = 0; i < size; i++) {
539                        looseUnmarshalString(dataIn);
540                        looseUnmarshalString(dataIn);
541                        looseUnmarshalString(dataIn);
542                        dataIn.readInt();
543                    }
544                }
545                o.initCause(looseUnmarsalThrowable(wireFormat, dataIn));
546
547            }
548            return o;
549        } else {
550            return null;
551        }
552    }
553
554    protected void looseMarshalThrowable(OpenWireFormat wireFormat, Throwable o, DataOutput dataOut)
555        throws IOException {
556        dataOut.writeBoolean(o != null);
557        if (o != null) {
558            looseMarshalString(o.getClass().getName(), dataOut);
559            looseMarshalString(o.getMessage(), dataOut);
560            if (wireFormat.isStackTraceEnabled()) {
561                StackTraceElement[] stackTrace = o.getStackTrace();
562                dataOut.writeShort(stackTrace.length);
563                for (int i = 0; i < stackTrace.length; i++) {
564                    StackTraceElement element = stackTrace[i];
565                    looseMarshalString(element.getClassName(), dataOut);
566                    looseMarshalString(element.getMethodName(), dataOut);
567                    looseMarshalString(element.getFileName(), dataOut);
568                    dataOut.writeInt(element.getLineNumber());
569                }
570                looseMarshalThrowable(wireFormat, o.getCause(), dataOut);
571            }
572        }
573    }
574
575    protected String looseUnmarshalString(DataInput dataIn) throws IOException {
576        if (dataIn.readBoolean()) {
577            return dataIn.readUTF();
578        } else {
579            return null;
580        }
581    }
582
583    protected void looseMarshalString(String value, DataOutput dataOut) throws IOException {
584        dataOut.writeBoolean(value != null);
585        if (value != null) {
586            dataOut.writeUTF(value);
587        }
588    }
589
590    protected void looseMarshalObjectArray(OpenWireFormat wireFormat, DataStructure[] objects,
591                                           DataOutput dataOut) throws IOException {
592        dataOut.writeBoolean(objects != null);
593        if (objects != null) {
594            dataOut.writeShort(objects.length);
595            for (int i = 0; i < objects.length; i++) {
596                looseMarshalNestedObject(wireFormat, objects[i], dataOut);
597            }
598        }
599    }
600
601    protected void looseMarshalConstByteArray(OpenWireFormat wireFormat, byte[] data, DataOutput dataOut,
602                                              int i) throws IOException {
603        dataOut.write(data, 0, i);
604    }
605
606    protected byte[] looseUnmarshalConstByteArray(DataInput dataIn, int i) throws IOException {
607        byte data[] = new byte[i];
608        dataIn.readFully(data);
609        return data;
610    }
611
612    protected void looseMarshalByteArray(OpenWireFormat wireFormat, byte[] data, DataOutput dataOut)
613        throws IOException {
614        dataOut.writeBoolean(data != null);
615        if (data != null) {
616            dataOut.writeInt(data.length);
617            dataOut.write(data);
618        }
619    }
620
621    protected byte[] looseUnmarshalByteArray(OpenWireFormat wireFormat, DataInput dataIn) throws IOException {
622        byte rc[] = null;
623        if (dataIn.readBoolean()) {
624            int size = dataIn.readInt();
625            OpenWireUtil.validateBufferSize(wireFormat, size);
626            rc = new byte[size];
627            dataIn.readFully(rc);
628        }
629        return rc;
630    }
631
632    protected void looseMarshalByteSequence(OpenWireFormat wireFormat, ByteSequence data, DataOutput dataOut)
633        throws IOException {
634        dataOut.writeBoolean(data != null);
635        if (data != null) {
636            dataOut.writeInt(data.getLength());
637            dataOut.write(data.getData(), data.getOffset(), data.getLength());
638        }
639    }
640
641    protected ByteSequence looseUnmarshalByteSequence(OpenWireFormat wireFormat, DataInput dataIn) throws IOException {
642        ByteSequence rc = null;
643        if (dataIn.readBoolean()) {
644            int size = dataIn.readInt();
645            OpenWireUtil.validateBufferSize(wireFormat, size);
646            byte[] t = new byte[size];
647            dataIn.readFully(t);
648            rc = new ByteSequence(t, 0, size);
649        }
650        return rc;
651    }
652}