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