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.v4;
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.openwire.OpenWireUtil;
029import org.apache.activemq.util.ByteSequence;
030
031public abstract class BaseDataStreamMarshaller implements DataStreamMarshaller {
032
033    public static final Constructor STACK_TRACE_ELEMENT_CONSTRUCTOR;
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                                                           Integer.valueOf(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            OpenWireUtil.validateIsThrowable(clazz);
233            Constructor constructor = clazz.getConstructor(new Class[] {String.class});
234            return (Throwable)constructor.newInstance(new Object[] {message});
235        } catch (IllegalArgumentException e) {
236            return e;
237        } catch (Throwable e) {
238            return new Throwable(className + ": " + message);
239        }
240    }
241
242    protected int tightMarshalThrowable1(OpenWireFormat wireFormat, Throwable o, BooleanStream bs)
243        throws IOException {
244        if (o == null) {
245            bs.writeBoolean(false);
246            return 0;
247        } else {
248            int rc = 0;
249            bs.writeBoolean(true);
250            rc += tightMarshalString1(o.getClass().getName(), bs);
251            rc += tightMarshalString1(o.getMessage(), bs);
252            if (wireFormat.isStackTraceEnabled()) {
253                rc += 2;
254                StackTraceElement[] stackTrace = o.getStackTrace();
255                for (int i = 0; i < stackTrace.length; i++) {
256                    StackTraceElement element = stackTrace[i];
257                    rc += tightMarshalString1(element.getClassName(), bs);
258                    rc += tightMarshalString1(element.getMethodName(), bs);
259                    rc += tightMarshalString1(element.getFileName(), bs);
260                    rc += 4;
261                }
262                rc += tightMarshalThrowable1(wireFormat, o.getCause(), bs);
263            }
264            return rc;
265        }
266    }
267
268    protected void tightMarshalThrowable2(OpenWireFormat wireFormat, Throwable o, DataOutput dataOut,
269                                          BooleanStream bs) throws IOException {
270        if (bs.readBoolean()) {
271            tightMarshalString2(o.getClass().getName(), dataOut, bs);
272            tightMarshalString2(o.getMessage(), dataOut, bs);
273            if (wireFormat.isStackTraceEnabled()) {
274                StackTraceElement[] stackTrace = o.getStackTrace();
275                dataOut.writeShort(stackTrace.length);
276                for (int i = 0; i < stackTrace.length; i++) {
277                    StackTraceElement element = stackTrace[i];
278                    tightMarshalString2(element.getClassName(), dataOut, bs);
279                    tightMarshalString2(element.getMethodName(), dataOut, bs);
280                    tightMarshalString2(element.getFileName(), dataOut, bs);
281                    dataOut.writeInt(element.getLineNumber());
282                }
283                tightMarshalThrowable2(wireFormat, o.getCause(), dataOut, bs);
284            }
285        }
286    }
287
288    @SuppressWarnings("deprecation")
289    protected String tightUnmarshalString(DataInput dataIn, BooleanStream bs) throws IOException {
290        if (bs.readBoolean()) {
291            if (bs.readBoolean()) {
292                int size = dataIn.readShort();
293                byte data[] = new byte[size];
294                dataIn.readFully(data);
295                // Yes deprecated, but we know what we are doing.
296                // This allows us to create a String from a ASCII byte array. (no UTF-8 decoding)
297                return new String(data, 0);
298            } else {
299                return dataIn.readUTF();
300            }
301        } else {
302            return null;
303        }
304    }
305
306    protected int tightMarshalString1(String value, BooleanStream bs) throws IOException {
307        bs.writeBoolean(value != null);
308        if (value != null) {
309
310            int strlen = value.length();
311            int utflen = 0;
312            char[] charr = new char[strlen];
313            int c = 0;
314            boolean isOnlyAscii = true;
315
316            value.getChars(0, strlen, charr, 0);
317
318            for (int i = 0; i < strlen; i++) {
319                c = charr[i];
320                if ((c >= 0x0001) && (c <= 0x007F)) {
321                    utflen++;
322                } else if (c > 0x07FF) {
323                    utflen += 3;
324                    isOnlyAscii = false;
325                } else {
326                    isOnlyAscii = false;
327                    utflen += 2;
328                }
329            }
330
331            if (utflen >= Short.MAX_VALUE) {
332                throw new IOException("Encountered a String value that is too long to encode.");
333            }
334            bs.writeBoolean(isOnlyAscii);
335            return utflen + 2;
336
337        } else {
338            return 0;
339        }
340    }
341
342    protected void tightMarshalString2(String value, DataOutput dataOut, BooleanStream bs) throws IOException {
343        if (bs.readBoolean()) {
344            // If we verified it only holds ascii values
345            if (bs.readBoolean()) {
346                dataOut.writeShort(value.length());
347                dataOut.writeBytes(value);
348            } else {
349                dataOut.writeUTF(value);
350            }
351        }
352    }
353
354    protected int tightMarshalObjectArray1(OpenWireFormat wireFormat, DataStructure[] objects,
355                                           BooleanStream bs) throws IOException {
356        if (objects != null) {
357            int rc = 0;
358            bs.writeBoolean(true);
359            rc += 2;
360            for (int i = 0; i < objects.length; i++) {
361                rc += tightMarshalNestedObject1(wireFormat, objects[i], bs);
362            }
363            return rc;
364        } else {
365            bs.writeBoolean(false);
366            return 0;
367        }
368    }
369
370    protected void tightMarshalObjectArray2(OpenWireFormat wireFormat, DataStructure[] objects,
371                                            DataOutput dataOut, BooleanStream bs) throws IOException {
372        if (bs.readBoolean()) {
373            dataOut.writeShort(objects.length);
374            for (int i = 0; i < objects.length; i++) {
375                tightMarshalNestedObject2(wireFormat, objects[i], dataOut, bs);
376            }
377        }
378    }
379
380    protected int tightMarshalConstByteArray1(byte[] data, BooleanStream bs, int i) throws IOException {
381        return i;
382    }
383
384    protected void tightMarshalConstByteArray2(byte[] data, DataOutput dataOut, BooleanStream bs, int i)
385        throws IOException {
386        dataOut.write(data, 0, i);
387    }
388
389    protected byte[] tightUnmarshalConstByteArray(DataInput dataIn, BooleanStream bs, int i)
390        throws IOException {
391        byte data[] = new byte[i];
392        dataIn.readFully(data);
393        return data;
394    }
395
396    protected int tightMarshalByteArray1(byte[] data, BooleanStream bs) throws IOException {
397        bs.writeBoolean(data != null);
398        if (data != null) {
399            return data.length + 4;
400        } else {
401            return 0;
402        }
403    }
404
405    protected void tightMarshalByteArray2(byte[] data, DataOutput dataOut, BooleanStream bs)
406        throws IOException {
407        if (bs.readBoolean()) {
408            dataOut.writeInt(data.length);
409            dataOut.write(data);
410        }
411    }
412
413    protected byte[] tightUnmarshalByteArray(DataInput dataIn, BooleanStream bs) throws IOException {
414        byte rc[] = null;
415        if (bs.readBoolean()) {
416            int size = dataIn.readInt();
417            rc = new byte[size];
418            dataIn.readFully(rc);
419        }
420        return rc;
421    }
422
423    protected int tightMarshalByteSequence1(ByteSequence data, BooleanStream bs) throws IOException {
424        bs.writeBoolean(data != null);
425        if (data != null) {
426            return data.getLength() + 4;
427        } else {
428            return 0;
429        }
430    }
431
432    protected void tightMarshalByteSequence2(ByteSequence data, DataOutput dataOut, BooleanStream bs)
433        throws IOException {
434        if (bs.readBoolean()) {
435            dataOut.writeInt(data.getLength());
436            dataOut.write(data.getData(), data.getOffset(), data.getLength());
437        }
438    }
439
440    protected ByteSequence tightUnmarshalByteSequence(DataInput dataIn, BooleanStream bs) throws IOException {
441        ByteSequence rc = null;
442        if (bs.readBoolean()) {
443            int size = dataIn.readInt();
444            byte[] t = new byte[size];
445            dataIn.readFully(t);
446            return new ByteSequence(t, 0, size);
447        }
448        return rc;
449    }
450
451    //
452    // The loose marshaling logic
453    //
454
455    public void looseMarshal(OpenWireFormat wireFormat, Object o, DataOutput dataOut) throws IOException {
456    }
457
458    public void looseUnmarshal(OpenWireFormat wireFormat, Object o, DataInput dataIn) throws IOException {
459    }
460
461    public void looseMarshalLong(OpenWireFormat wireFormat, long o, DataOutput dataOut) throws IOException {
462        dataOut.writeLong(o);
463    }
464
465    public long looseUnmarshalLong(OpenWireFormat wireFormat, DataInput dataIn) throws IOException {
466        return dataIn.readLong();
467    }
468
469    protected DataStructure looseUnmarsalNestedObject(OpenWireFormat wireFormat, DataInput dataIn)
470        throws IOException {
471        return wireFormat.looseUnmarshalNestedObject(dataIn);
472    }
473
474    protected void looseMarshalNestedObject(OpenWireFormat wireFormat, DataStructure o, DataOutput dataOut)
475        throws IOException {
476        wireFormat.looseMarshalNestedObject(o, dataOut);
477    }
478
479    protected DataStructure looseUnmarsalCachedObject(OpenWireFormat wireFormat, DataInput dataIn)
480        throws IOException {
481        if (wireFormat.isCacheEnabled()) {
482            if (dataIn.readBoolean()) {
483                short index = dataIn.readShort();
484                DataStructure object = wireFormat.looseUnmarshalNestedObject(dataIn);
485                wireFormat.setInUnmarshallCache(index, object);
486                return object;
487            } else {
488                short index = dataIn.readShort();
489                return wireFormat.getFromUnmarshallCache(index);
490            }
491        } else {
492            return wireFormat.looseUnmarshalNestedObject(dataIn);
493        }
494    }
495
496    protected void looseMarshalCachedObject(OpenWireFormat wireFormat, DataStructure o, DataOutput dataOut)
497        throws IOException {
498        if (wireFormat.isCacheEnabled()) {
499            Short index = wireFormat.getMarshallCacheIndex(o);
500            dataOut.writeBoolean(index == null);
501            if (index == null) {
502                index = wireFormat.addToMarshallCache(o);
503                dataOut.writeShort(index.shortValue());
504                wireFormat.looseMarshalNestedObject(o, dataOut);
505            } else {
506                dataOut.writeShort(index.shortValue());
507            }
508        } else {
509            wireFormat.looseMarshalNestedObject(o, dataOut);
510        }
511    }
512
513    protected Throwable looseUnmarsalThrowable(OpenWireFormat wireFormat, DataInput dataIn)
514        throws IOException {
515        if (dataIn.readBoolean()) {
516            String clazz = looseUnmarshalString(dataIn);
517            String message = looseUnmarshalString(dataIn);
518            Throwable o = createThrowable(clazz, message);
519            if (wireFormat.isStackTraceEnabled()) {
520                if (STACK_TRACE_ELEMENT_CONSTRUCTOR != null) {
521                    StackTraceElement ss[] = new StackTraceElement[dataIn.readShort()];
522                    for (int i = 0; i < ss.length; i++) {
523                        try {
524                            ss[i] = (StackTraceElement)STACK_TRACE_ELEMENT_CONSTRUCTOR
525                                .newInstance(new Object[] {looseUnmarshalString(dataIn),
526                                                           looseUnmarshalString(dataIn),
527                                                           looseUnmarshalString(dataIn),
528                                                           Integer.valueOf(dataIn.readInt())});
529                        } catch (IOException e) {
530                            throw e;
531                        } catch (Throwable e) {
532                        }
533                    }
534                    o.setStackTrace(ss);
535                } else {
536                    short size = dataIn.readShort();
537                    for (int i = 0; i < size; i++) {
538                        looseUnmarshalString(dataIn);
539                        looseUnmarshalString(dataIn);
540                        looseUnmarshalString(dataIn);
541                        dataIn.readInt();
542                    }
543                }
544                o.initCause(looseUnmarsalThrowable(wireFormat, dataIn));
545
546            }
547            return o;
548        } else {
549            return null;
550        }
551    }
552
553    protected void looseMarshalThrowable(OpenWireFormat wireFormat, Throwable o, DataOutput dataOut)
554        throws IOException {
555        dataOut.writeBoolean(o != null);
556        if (o != null) {
557            looseMarshalString(o.getClass().getName(), dataOut);
558            looseMarshalString(o.getMessage(), dataOut);
559            if (wireFormat.isStackTraceEnabled()) {
560                StackTraceElement[] stackTrace = o.getStackTrace();
561                dataOut.writeShort(stackTrace.length);
562                for (int i = 0; i < stackTrace.length; i++) {
563                    StackTraceElement element = stackTrace[i];
564                    looseMarshalString(element.getClassName(), dataOut);
565                    looseMarshalString(element.getMethodName(), dataOut);
566                    looseMarshalString(element.getFileName(), dataOut);
567                    dataOut.writeInt(element.getLineNumber());
568                }
569                looseMarshalThrowable(wireFormat, o.getCause(), dataOut);
570            }
571        }
572    }
573
574    protected String looseUnmarshalString(DataInput dataIn) throws IOException {
575        if (dataIn.readBoolean()) {
576            return dataIn.readUTF();
577        } else {
578            return null;
579        }
580    }
581
582    protected void looseMarshalString(String value, DataOutput dataOut) throws IOException {
583        dataOut.writeBoolean(value != null);
584        if (value != null) {
585            dataOut.writeUTF(value);
586        }
587    }
588
589    protected void looseMarshalObjectArray(OpenWireFormat wireFormat, DataStructure[] objects,
590                                           DataOutput dataOut) throws IOException {
591        dataOut.writeBoolean(objects != null);
592        if (objects != null) {
593            dataOut.writeShort(objects.length);
594            for (int i = 0; i < objects.length; i++) {
595                looseMarshalNestedObject(wireFormat, objects[i], dataOut);
596            }
597        }
598    }
599
600    protected void looseMarshalConstByteArray(OpenWireFormat wireFormat, byte[] data, DataOutput dataOut,
601                                              int i) throws IOException {
602        dataOut.write(data, 0, i);
603    }
604
605    protected byte[] looseUnmarshalConstByteArray(DataInput dataIn, int i) throws IOException {
606        byte data[] = new byte[i];
607        dataIn.readFully(data);
608        return data;
609    }
610
611    protected void looseMarshalByteArray(OpenWireFormat wireFormat, byte[] data, DataOutput dataOut)
612        throws IOException {
613        dataOut.writeBoolean(data != null);
614        if (data != null) {
615            dataOut.writeInt(data.length);
616            dataOut.write(data);
617        }
618    }
619
620    protected byte[] looseUnmarshalByteArray(DataInput dataIn) throws IOException {
621        byte rc[] = null;
622        if (dataIn.readBoolean()) {
623            int size = dataIn.readInt();
624            rc = new byte[size];
625            dataIn.readFully(rc);
626        }
627        return rc;
628    }
629
630    protected void looseMarshalByteSequence(OpenWireFormat wireFormat, ByteSequence data, DataOutput dataOut)
631        throws IOException {
632        dataOut.writeBoolean(data != null);
633        if (data != null) {
634            dataOut.writeInt(data.getLength());
635            dataOut.write(data.getData(), data.getOffset(), data.getLength());
636        }
637    }
638
639    protected ByteSequence looseUnmarshalByteSequence(DataInput dataIn) throws IOException {
640        ByteSequence rc = null;
641        if (dataIn.readBoolean()) {
642            int size = dataIn.readInt();
643            byte[] t = new byte[size];
644            dataIn.readFully(t);
645            rc = new ByteSequence(t, 0, size);
646        }
647        return rc;
648    }
649}