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