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.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(DataInput dataIn, BooleanStream bs) throws IOException {
413        byte rc[] = null;
414        if (bs.readBoolean()) {
415            int size = dataIn.readInt();
416            rc = new byte[size];
417            dataIn.readFully(rc);
418        }
419        return rc;
420    }
421
422    protected int tightMarshalByteSequence1(ByteSequence data, BooleanStream bs) throws IOException {
423        bs.writeBoolean(data != null);
424        if (data != null) {
425            return data.getLength() + 4;
426        } else {
427            return 0;
428        }
429    }
430
431    protected void tightMarshalByteSequence2(ByteSequence data, DataOutput dataOut, BooleanStream bs)
432        throws IOException {
433        if (bs.readBoolean()) {
434            dataOut.writeInt(data.getLength());
435            dataOut.write(data.getData(), data.getOffset(), data.getLength());
436        }
437    }
438
439    protected ByteSequence tightUnmarshalByteSequence(DataInput dataIn, BooleanStream bs) throws IOException {
440        ByteSequence rc = null;
441        if (bs.readBoolean()) {
442            int size = dataIn.readInt();
443            byte[] t = new byte[size];
444            dataIn.readFully(t);
445            return new ByteSequence(t, 0, size);
446        }
447        return rc;
448    }
449
450    //
451    // The loose marshaling logic
452    //
453
454    public void looseMarshal(OpenWireFormat wireFormat, Object o, DataOutput dataOut) throws IOException {
455    }
456
457    public void looseUnmarshal(OpenWireFormat wireFormat, Object o, DataInput dataIn) throws IOException {
458    }
459
460    public void looseMarshalLong(OpenWireFormat wireFormat, long o, DataOutput dataOut) throws IOException {
461        dataOut.writeLong(o);
462    }
463
464    public long looseUnmarshalLong(OpenWireFormat wireFormat, DataInput dataIn) throws IOException {
465        return dataIn.readLong();
466    }
467
468    protected DataStructure looseUnmarsalNestedObject(OpenWireFormat wireFormat, DataInput dataIn)
469        throws IOException {
470        return wireFormat.looseUnmarshalNestedObject(dataIn);
471    }
472
473    protected void looseMarshalNestedObject(OpenWireFormat wireFormat, DataStructure o, DataOutput dataOut)
474        throws IOException {
475        wireFormat.looseMarshalNestedObject(o, dataOut);
476    }
477
478    protected DataStructure looseUnmarsalCachedObject(OpenWireFormat wireFormat, DataInput dataIn)
479        throws IOException {
480        if (wireFormat.isCacheEnabled()) {
481            if (dataIn.readBoolean()) {
482                short index = dataIn.readShort();
483                DataStructure object = wireFormat.looseUnmarshalNestedObject(dataIn);
484                wireFormat.setInUnmarshallCache(index, object);
485                return object;
486            } else {
487                short index = dataIn.readShort();
488                return wireFormat.getFromUnmarshallCache(index);
489            }
490        } else {
491            return wireFormat.looseUnmarshalNestedObject(dataIn);
492        }
493    }
494
495    protected void looseMarshalCachedObject(OpenWireFormat wireFormat, DataStructure o, DataOutput dataOut)
496        throws IOException {
497        if (wireFormat.isCacheEnabled()) {
498            Short index = wireFormat.getMarshallCacheIndex(o);
499            dataOut.writeBoolean(index == null);
500            if (index == null) {
501                index = wireFormat.addToMarshallCache(o);
502                dataOut.writeShort(index.shortValue());
503                wireFormat.looseMarshalNestedObject(o, dataOut);
504            } else {
505                dataOut.writeShort(index.shortValue());
506            }
507        } else {
508            wireFormat.looseMarshalNestedObject(o, dataOut);
509        }
510    }
511
512    protected Throwable looseUnmarsalThrowable(OpenWireFormat wireFormat, DataInput dataIn)
513        throws IOException {
514        if (dataIn.readBoolean()) {
515            String clazz = looseUnmarshalString(dataIn);
516            String message = looseUnmarshalString(dataIn);
517            Throwable o = createThrowable(clazz, message);
518            if (wireFormat.isStackTraceEnabled()) {
519                if (STACK_TRACE_ELEMENT_CONSTRUCTOR != null) {
520                    StackTraceElement ss[] = new StackTraceElement[dataIn.readShort()];
521                    for (int i = 0; i < ss.length; i++) {
522                        try {
523                            ss[i] = (StackTraceElement)STACK_TRACE_ELEMENT_CONSTRUCTOR
524                                .newInstance(new Object[] {looseUnmarshalString(dataIn),
525                                                           looseUnmarshalString(dataIn),
526                                                           looseUnmarshalString(dataIn),
527                                                           Integer.valueOf(dataIn.readInt())});
528                        } catch (IOException e) {
529                            throw e;
530                        } catch (Throwable e) {
531                        }
532                    }
533                    o.setStackTrace(ss);
534                } else {
535                    short size = dataIn.readShort();
536                    for (int i = 0; i < size; i++) {
537                        looseUnmarshalString(dataIn);
538                        looseUnmarshalString(dataIn);
539                        looseUnmarshalString(dataIn);
540                        dataIn.readInt();
541                    }
542                }
543                o.initCause(looseUnmarsalThrowable(wireFormat, dataIn));
544
545            }
546            return o;
547        } else {
548            return null;
549        }
550    }
551
552    protected void looseMarshalThrowable(OpenWireFormat wireFormat, Throwable o, DataOutput dataOut)
553        throws IOException {
554        dataOut.writeBoolean(o != null);
555        if (o != null) {
556            looseMarshalString(o.getClass().getName(), dataOut);
557            looseMarshalString(o.getMessage(), dataOut);
558            if (wireFormat.isStackTraceEnabled()) {
559                StackTraceElement[] stackTrace = o.getStackTrace();
560                dataOut.writeShort(stackTrace.length);
561                for (int i = 0; i < stackTrace.length; i++) {
562                    StackTraceElement element = stackTrace[i];
563                    looseMarshalString(element.getClassName(), dataOut);
564                    looseMarshalString(element.getMethodName(), dataOut);
565                    looseMarshalString(element.getFileName(), dataOut);
566                    dataOut.writeInt(element.getLineNumber());
567                }
568                looseMarshalThrowable(wireFormat, o.getCause(), dataOut);
569            }
570        }
571    }
572
573    protected String looseUnmarshalString(DataInput dataIn) throws IOException {
574        if (dataIn.readBoolean()) {
575            return dataIn.readUTF();
576        } else {
577            return null;
578        }
579    }
580
581    protected void looseMarshalString(String value, DataOutput dataOut) throws IOException {
582        dataOut.writeBoolean(value != null);
583        if (value != null) {
584            dataOut.writeUTF(value);
585        }
586    }
587
588    protected void looseMarshalObjectArray(OpenWireFormat wireFormat, DataStructure[] objects,
589                                           DataOutput dataOut) throws IOException {
590        dataOut.writeBoolean(objects != null);
591        if (objects != null) {
592            dataOut.writeShort(objects.length);
593            for (int i = 0; i < objects.length; i++) {
594                looseMarshalNestedObject(wireFormat, objects[i], dataOut);
595            }
596        }
597    }
598
599    protected void looseMarshalConstByteArray(OpenWireFormat wireFormat, byte[] data, DataOutput dataOut,
600                                              int i) throws IOException {
601        dataOut.write(data, 0, i);
602    }
603
604    protected byte[] looseUnmarshalConstByteArray(DataInput dataIn, int i) throws IOException {
605        byte data[] = new byte[i];
606        dataIn.readFully(data);
607        return data;
608    }
609
610    protected void looseMarshalByteArray(OpenWireFormat wireFormat, byte[] data, DataOutput dataOut)
611        throws IOException {
612        dataOut.writeBoolean(data != null);
613        if (data != null) {
614            dataOut.writeInt(data.length);
615            dataOut.write(data);
616        }
617    }
618
619    protected byte[] looseUnmarshalByteArray(DataInput dataIn) throws IOException {
620        byte rc[] = null;
621        if (dataIn.readBoolean()) {
622            int size = dataIn.readInt();
623            rc = new byte[size];
624            dataIn.readFully(rc);
625        }
626        return rc;
627    }
628
629    protected void looseMarshalByteSequence(OpenWireFormat wireFormat, ByteSequence data, DataOutput dataOut)
630        throws IOException {
631        dataOut.writeBoolean(data != null);
632        if (data != null) {
633            dataOut.writeInt(data.getLength());
634            dataOut.write(data.getData(), data.getOffset(), data.getLength());
635        }
636    }
637
638    protected ByteSequence looseUnmarshalByteSequence(DataInput dataIn) throws IOException {
639        ByteSequence rc = null;
640        if (dataIn.readBoolean()) {
641            int size = dataIn.readInt();
642            byte[] t = new byte[size];
643            dataIn.readFully(t);
644            rc = new ByteSequence(t, 0, size);
645        }
646        return rc;
647    }
648}