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