• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

wuwen5 / hessian / 18043144273

25 Sep 2025 12:46AM UTC coverage: 71.311% (+0.2%) from 71.118%
18043144273

push

github

web-flow
Refactor JavaSerializer, UnsafeSerializer, and BeanSerializer to eliminate code duplication (#57)

* Initial plan

* Refactor JavaSerializer and UnsafeSerializer to use FieldBasedSerializer base class

Co-authored-by: wuwen5 <5037807+wuwen5@users.noreply.github.com>

* Complete refactoring - consolidate writeReplace methods and fix module access

Co-authored-by: wuwen5 <5037807+wuwen5@users.noreply.github.com>

* Address code review feedback: move setAccessible to JavaSerializer only, remove writeObject10 from base classes

Co-authored-by: wuwen5 <5037807+wuwen5@users.noreply.github.com>

* Refactor JavaSerializer to reuse parent introspection and remove unused writeFieldValue method

Co-authored-by: wuwen5 <5037807+wuwen5@users.noreply.github.com>

* Remove unused writeObject10 methods and writeReplaceFactory field

Co-authored-by: wuwen5 <5037807+wuwen5@users.noreply.github.com>

* Rename writeReplace field to writeReplaceMethod to avoid confusion with superclass method

Co-authored-by: wuwen5 <5037807+wuwen5@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: wuwen5 <5037807+wuwen5@users.noreply.github.com>

1724 of 2605 branches covered (66.18%)

Branch coverage included in aggregate %.

4152 of 5635 relevant lines covered (73.68%)

3.18 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

84.5
hessian2-codec/src/main/java/io/github/wuwen5/hessian/io/UnsafeSerializer.java
1
/*
2
 * Copyright (c) 2001-2008 Caucho Technology, Inc.  All rights reserved.
3
 *
4
 * The Apache Software License, Version 1.1
5
 *
6
 * Redistribution and use in source and binary forms, with or without
7
 * modification, are permitted provided that the following conditions
8
 * are met:
9
 *
10
 * 1. Redistributions of source code must retain the above copyright
11
 *    notice, this list of conditions and the following disclaimer.
12
 *
13
 * 2. Redistributions in binary form must reproduce the above copyright
14
 *    notice, this list of conditions and the following disclaimer in
15
 *    the documentation and/or other materials provided with the
16
 *    distribution.
17
 *
18
 * 3. The end-user documentation included with the redistribution, if
19
 *    any, must include the following acknowlegement:
20
 *       "This product includes software developed by the
21
 *        Caucho Technology (http://www.caucho.com/)."
22
 *    Alternately, this acknowlegement may appear in the software itself,
23
 *    if and wherever such third-party acknowlegements normally appear.
24
 *
25
 * 4. The names "Burlap", "Resin", and "Caucho" must not be used to
26
 *    endorse or promote products derived from this software without prior
27
 *    written permission. For written permission, please contact
28
 *    info@caucho.com.
29
 *
30
 * 5. Products derived from this software may not be called "Resin"
31
 *    nor may "Resin" appear in their names without prior written
32
 *    permission of Caucho Technology.
33
 *
34
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
35
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
36
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
37
 * DISCLAIMED.  IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS
38
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
39
 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
40
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
41
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
42
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
43
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
44
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
45
 *
46
 * @author Scott Ferguson
47
 */
48

49
package io.github.wuwen5.hessian.io;
50

51
import com.caucho.hessian.HessianUnshared;
52
import java.io.IOException;
53
import java.lang.ref.SoftReference;
54
import java.lang.reflect.Field;
55
import java.util.WeakHashMap;
56
import lombok.extern.slf4j.Slf4j;
57
import sun.misc.Unsafe;
58

59
/**
60
 * Serializing an object for known object types.
61
 */
62
@Slf4j
3✔
63
public class UnsafeSerializer extends FieldBasedSerializer {
64
    private static boolean isEnabled;
65
    private static final Unsafe UNSAFE;
66

67
    private static final WeakHashMap<Class<?>, SoftReference<UnsafeSerializer>> SERIALIZER_MAP = new WeakHashMap<>();
4✔
68

69
    private FieldSerializer[] fieldSerializers;
70

71
    public static boolean isEnabled() {
72
        return isEnabled;
2✔
73
    }
74

75
    public UnsafeSerializer(Class<?> cl) {
2✔
76
        introspectFields(cl);
3✔
77

78
        fieldSerializers = new FieldSerializer[this.fields.length];
6✔
79

80
        for (int i = 0; i < this.fields.length; i++) {
9✔
81
            fieldSerializers[i] = getFieldSerializer(this.fields[i]);
9✔
82
        }
83
    }
1✔
84

85
    /**
86
     * UnsafeSerializer doesn't use writeReplace, so override the base implementation
87
     */
88
    @Override
89
    public void writeObject(Object obj, AbstractHessianEncoder out) throws IOException {
90
        if (out.addRef(obj)) {
4✔
91
            return;
1✔
92
        }
93

94
        Class<?> cl = obj.getClass();
3✔
95

96
        int ref = out.writeObjectBegin(cl.getName());
5✔
97

98
        if (ref >= 0) {
2✔
99
            writeInstance(obj, out);
5✔
100
        } else if (ref == -1) {
3!
101
            writeDefinition20(out);
3✔
102
            out.writeObjectBegin(cl.getName());
5✔
103
            writeInstance(obj, out);
4✔
104
        }
105
    }
1✔
106

107
    public static UnsafeSerializer create(Class<?> cl) {
108
        synchronized (SERIALIZER_MAP) {
4✔
109
            SoftReference<UnsafeSerializer> baseRef = SERIALIZER_MAP.get(cl);
5✔
110

111
            UnsafeSerializer base = baseRef != null ? baseRef.get() : null;
8✔
112

113
            if (base == null) {
2✔
114
                if (cl.isAnnotationPresent(HessianUnshared.class)
6✔
115
                        || cl.isAnnotationPresent(io.github.wuwen5.hessian.HessianUnshared.class)) {
2!
116
                    base = new UnsafeUnsharedSerializer(cl);
6✔
117
                } else {
118
                    base = new UnsafeSerializer(cl);
5✔
119
                }
120

121
                baseRef = new SoftReference<>(base);
5✔
122
                SERIALIZER_MAP.put(cl, baseRef);
5✔
123
            }
124

125
            return base;
4✔
126
        }
127
    }
128

129
    /**
130
     * Implement abstract method from FieldBasedSerializer
131
     */
132
    @Override
133
    protected void writeInstanceFields(Object obj, AbstractHessianEncoder out) throws IOException {
134
        for (FieldSerializer fieldSerializer : this.fieldSerializers) {
17✔
135
            fieldSerializer.serialize(out, obj);
4✔
136
        }
137
    }
1✔
138

139
    private static FieldSerializer getFieldSerializer(Field field) {
140
        Class<?> type = field.getType();
3✔
141

142
        if (boolean.class.equals(type)) {
4✔
143
            return new BooleanFieldSerializer(field);
5✔
144
        } else if (byte.class.equals(type)) {
4✔
145
            return new ByteFieldSerializer(field);
5✔
146
        } else if (char.class.equals(type)) {
4✔
147
            return new CharFieldSerializer(field);
5✔
148
        } else if (short.class.equals(type)) {
4✔
149
            return new ShortFieldSerializer(field);
5✔
150
        } else if (int.class.equals(type)) {
4✔
151
            return new IntFieldSerializer(field);
5✔
152
        } else if (long.class.equals(type)) {
4✔
153
            return new LongFieldSerializer(field);
5✔
154
        } else if (double.class.equals(type)) {
4✔
155
            return new DoubleFieldSerializer(field);
5✔
156
        } else if (float.class.equals(type)) {
4✔
157
            return new FloatFieldSerializer(field);
5✔
158
        } else if (String.class.equals(type)) {
4✔
159
            return new StringFieldSerializer(field);
5✔
160
        } else if (java.util.Date.class.equals(type)
6✔
161
                || java.sql.Date.class.equals(type)
4✔
162
                || java.sql.Timestamp.class.equals(type)
4✔
163
                || java.sql.Time.class.equals(type)) {
2✔
164
            return new DateFieldSerializer(field);
5✔
165
        } else {
166
            return new ObjectFieldSerializer(field);
5✔
167
        }
168
    }
169

170
    abstract static class FieldSerializer {
3✔
171
        abstract void serialize(AbstractHessianEncoder out, Object obj) throws IOException;
172
    }
173

174
    static final class ObjectFieldSerializer extends FieldSerializer {
175
        private final Field field;
176
        private final long offset;
177

178
        ObjectFieldSerializer(Field field) {
2✔
179
            this.field = field;
3✔
180
            offset = UNSAFE.objectFieldOffset(field);
5✔
181

182
            if (offset == Unsafe.INVALID_FIELD_OFFSET) throw new IllegalStateException();
5!
183
        }
1✔
184

185
        @Override
186
        void serialize(AbstractHessianEncoder out, Object obj) throws IOException {
187
            try {
188
                Object value = UNSAFE.getObject(obj, offset);
6✔
189

190
                out.writeObject(value);
3✔
191
            } catch (RuntimeException e) {
×
192
                throw new IllegalStateException(
×
193
                        e.getMessage() + "\n field: "
×
194
                                + field.getDeclaringClass().getName()
×
195
                                + '.' + field.getName(),
×
196
                        e);
197
            } catch (IOException e) {
×
198
                throw new IOExceptionWrapper(
×
199
                        e.getMessage() + "\n field: "
×
200
                                + field.getDeclaringClass().getName()
×
201
                                + '.' + field.getName(),
×
202
                        e);
203
            }
1✔
204
        }
1✔
205
    }
206

207
    static final class BooleanFieldSerializer extends FieldSerializer {
208
        private final long offset;
209

210
        BooleanFieldSerializer(Field field) {
2✔
211
            offset = UNSAFE.objectFieldOffset(field);
5✔
212

213
            if (offset == Unsafe.INVALID_FIELD_OFFSET) {
5!
214
                throw new IllegalStateException();
×
215
            }
216
        }
1✔
217

218
        @Override
219
        void serialize(AbstractHessianEncoder out, Object obj) throws IOException {
220
            boolean value = UNSAFE.getBoolean(obj, offset);
6✔
221

222
            out.writeBoolean(value);
3✔
223
        }
1✔
224
    }
225

226
    static final class ByteFieldSerializer extends FieldSerializer {
227
        private final long offset;
228

229
        ByteFieldSerializer(Field field) {
2✔
230
            offset = UNSAFE.objectFieldOffset(field);
5✔
231

232
            if (offset == Unsafe.INVALID_FIELD_OFFSET) {
5!
233
                throw new IllegalStateException();
×
234
            }
235
        }
1✔
236

237
        @Override
238
        void serialize(AbstractHessianEncoder out, Object obj) throws IOException {
239
            int value = UNSAFE.getByte(obj, offset);
6✔
240

241
            out.writeInt(value);
3✔
242
        }
1✔
243
    }
244

245
    static final class CharFieldSerializer extends FieldSerializer {
246
        private final long offset;
247

248
        CharFieldSerializer(Field field) {
2✔
249
            offset = UNSAFE.objectFieldOffset(field);
5✔
250

251
            if (offset == Unsafe.INVALID_FIELD_OFFSET) {
5!
252
                throw new IllegalStateException();
×
253
            }
254
        }
1✔
255

256
        @Override
257
        void serialize(AbstractHessianEncoder out, Object obj) throws IOException {
258
            char value = UNSAFE.getChar(obj, offset);
6✔
259

260
            out.writeString(String.valueOf(value));
4✔
261
        }
1✔
262
    }
263

264
    static final class ShortFieldSerializer extends FieldSerializer {
265
        private final long offset;
266

267
        ShortFieldSerializer(Field field) {
2✔
268
            offset = UNSAFE.objectFieldOffset(field);
5✔
269

270
            if (offset == Unsafe.INVALID_FIELD_OFFSET) {
5!
271
                throw new IllegalStateException();
×
272
            }
273
        }
1✔
274

275
        @Override
276
        void serialize(AbstractHessianEncoder out, Object obj) throws IOException {
277
            int value = UNSAFE.getShort(obj, offset);
6✔
278

279
            out.writeInt(value);
3✔
280
        }
1✔
281
    }
282

283
    static final class IntFieldSerializer extends FieldSerializer {
284
        private final long offset;
285

286
        IntFieldSerializer(Field field) {
2✔
287
            offset = UNSAFE.objectFieldOffset(field);
5✔
288

289
            if (offset == Unsafe.INVALID_FIELD_OFFSET) {
5!
290
                throw new IllegalStateException();
×
291
            }
292
        }
1✔
293

294
        @Override
295
        void serialize(AbstractHessianEncoder out, Object obj) throws IOException {
296
            int value = UNSAFE.getInt(obj, offset);
6✔
297

298
            out.writeInt(value);
3✔
299
        }
1✔
300
    }
301

302
    static final class LongFieldSerializer extends FieldSerializer {
303
        private final long offset;
304

305
        LongFieldSerializer(Field field) {
2✔
306
            offset = UNSAFE.objectFieldOffset(field);
5✔
307

308
            if (offset == Unsafe.INVALID_FIELD_OFFSET) {
5!
309
                throw new IllegalStateException();
×
310
            }
311
        }
1✔
312

313
        @Override
314
        void serialize(AbstractHessianEncoder out, Object obj) throws IOException {
315
            long value = UNSAFE.getLong(obj, offset);
6✔
316

317
            out.writeLong(value);
3✔
318
        }
1✔
319
    }
320

321
    static final class FloatFieldSerializer extends FieldSerializer {
322
        private final long offset;
323

324
        FloatFieldSerializer(Field field) {
2✔
325
            offset = UNSAFE.objectFieldOffset(field);
5✔
326

327
            if (offset == Unsafe.INVALID_FIELD_OFFSET) {
5!
328
                throw new IllegalStateException();
×
329
            }
330
        }
1✔
331

332
        @Override
333
        void serialize(AbstractHessianEncoder out, Object obj) throws IOException {
334
            double value = UNSAFE.getFloat(obj, offset);
7✔
335

336
            out.writeDouble(value);
3✔
337
        }
1✔
338
    }
339

340
    static final class DoubleFieldSerializer extends FieldSerializer {
341
        private final long offset;
342

343
        DoubleFieldSerializer(Field field) {
2✔
344
            offset = UNSAFE.objectFieldOffset(field);
5✔
345

346
            if (offset == Unsafe.INVALID_FIELD_OFFSET) throw new IllegalStateException();
5!
347
        }
1✔
348

349
        @Override
350
        void serialize(AbstractHessianEncoder out, Object obj) throws IOException {
351
            double value = UNSAFE.getDouble(obj, offset);
6✔
352

353
            out.writeDouble(value);
3✔
354
        }
1✔
355
    }
356

357
    static final class StringFieldSerializer extends FieldSerializer {
358
        private final long offset;
359

360
        StringFieldSerializer(Field field) {
2✔
361
            offset = UNSAFE.objectFieldOffset(field);
5✔
362

363
            if (offset == Unsafe.INVALID_FIELD_OFFSET) {
5!
364
                throw new IllegalStateException();
×
365
            }
366
        }
1✔
367

368
        @Override
369
        void serialize(AbstractHessianEncoder out, Object obj) throws IOException {
370
            String value = (String) UNSAFE.getObject(obj, offset);
7✔
371

372
            out.writeString(value);
3✔
373
        }
1✔
374
    }
375

376
    static final class DateFieldSerializer extends FieldSerializer {
377
        private final long offset;
378

379
        DateFieldSerializer(Field field) {
2✔
380
            offset = UNSAFE.objectFieldOffset(field);
5✔
381

382
            if (offset == Unsafe.INVALID_FIELD_OFFSET) {
5!
383
                throw new IllegalStateException();
×
384
            }
385
        }
1✔
386

387
        @Override
388
        void serialize(AbstractHessianEncoder out, Object obj) throws IOException {
389
            java.util.Date value = (java.util.Date) UNSAFE.getObject(obj, offset);
7✔
390

391
            if (value == null) {
2!
392
                out.writeNull();
×
393
            } else {
394
                out.writeUTCDate(value.getTime());
4✔
395
            }
396
        }
1✔
397
    }
398

399
    static {
400
        boolean isEnabled = false;
2✔
401
        Unsafe unsafe = null;
2✔
402

403
        try {
404
            Class<?> unsafeClass = Class.forName("sun.misc.Unsafe");
3✔
405
            Field theUnsafe = null;
2✔
406
            for (Field field : unsafeClass.getDeclaredFields()) {
17✔
407
                if ("theUnsafe".equals(field.getName())) {
5✔
408
                    theUnsafe = field;
2✔
409
                }
410
            }
411

412
            if (theUnsafe != null) {
2!
413
                theUnsafe.setAccessible(true);
3✔
414
                unsafe = (Unsafe) theUnsafe.get(null);
5✔
415
            }
416

417
            isEnabled = unsafe != null;
5!
418

419
            String unsafeProp = System.getProperty("com.caucho.hessian.unsafe");
3✔
420

421
            if ("false".equals(unsafeProp)) {
4!
422
                isEnabled = false;
×
423
            }
424
        } catch (Throwable e) {
×
425
            log.error(e.toString(), e);
×
426
        }
1✔
427

428
        UNSAFE = unsafe;
2✔
429
        UnsafeSerializer.isEnabled = isEnabled;
2✔
430
    }
1✔
431
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc