• 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

73.55
hessian2-codec/src/main/java/io/github/wuwen5/hessian/io/BeanSerializer.java
1
/*
2
 * Copyright (c) 2001-2004 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 java.io.IOException;
52
import java.lang.reflect.Method;
53
import java.lang.reflect.Modifier;
54
import java.util.ArrayList;
55
import java.util.Comparator;
56
import java.util.List;
57
import java.util.Locale;
58
import lombok.extern.slf4j.Slf4j;
59

60
/**
61
 * Serializing an object for known object types.
62
 */
63
@Slf4j
4✔
64
public class BeanSerializer extends AbstractSerializer {
65

66
    private final Method[] methods;
67
    private final String[] names;
68

69
    private final MethodSerializer[] methodSerializers;
70

71
    private Object writeReplaceFactory;
72
    private Method writeReplace;
73

74
    public BeanSerializer(Class<?> cl, ClassLoader loader) {
2✔
75
        introspectWriteReplace(cl, loader);
4✔
76

77
        List<Method> primitiveMethods = new ArrayList<>();
4✔
78
        List<Method> compoundMethods = new ArrayList<>();
4✔
79

80
        for (; cl != null; cl = cl.getSuperclass()) {
6✔
81
            Method[] declaredMethods = cl.getDeclaredMethods();
3✔
82

83
            for (Method method : declaredMethods) {
16✔
84

85
                String name = method.getName();
3✔
86
                Class<?> type = method.getReturnType();
3✔
87
                if (!Modifier.isStatic(method.getModifiers())
6✔
88
                        && !type.equals(void.class)
3✔
89
                        && method.getParameterTypes().length == 0) {
3✔
90
                    boolean isGetter = name.startsWith("get") || name.startsWith("is");
12✔
91
                    if (isGetter && existsSetter(declaredMethods, name, type)) {
8✔
92

93
                        if (type.isPrimitive()
4✔
94
                                || type.getName().startsWith("java.lang.") && !type.equals(Object.class)) {
8!
95
                            primitiveMethods.add(method);
5✔
96
                        } else {
97
                            compoundMethods.add(method);
×
98
                        }
99
                    }
100
                }
101
            }
102
        }
103

104
        List<Method> methodList = new ArrayList<>();
4✔
105
        methodList.addAll(primitiveMethods);
4✔
106
        methodList.addAll(compoundMethods);
4✔
107

108
        methodList.sort(new MethodNameCmp());
5✔
109

110
        methods = new Method[methodList.size()];
5✔
111
        methodList.toArray(methods);
5✔
112

113
        names = new String[methods.length];
6✔
114

115
        for (int i = 0; i < methods.length; i++) {
9✔
116
            String name = methods[i].getName();
6✔
117

118
            name = name.startsWith("is") ? name.substring(2) : name.substring(3);
12✔
119

120
            int j = 0;
2✔
121
            for (; j < name.length() && Character.isUpperCase(name.charAt(j)); j++) {}
11!
122

123
            if (j == 1) {
3!
124
                name = name.substring(0, j).toLowerCase(Locale.ENGLISH) + name.substring(j);
12✔
125
            } else if (j > 1) {
×
126
                name = name.substring(0, j - 1).toLowerCase(Locale.ENGLISH) + name.substring(j - 1);
×
127
            }
128

129
            names[i] = name;
5✔
130
        }
131

132
        methodSerializers = new MethodSerializer[this.methods.length];
6✔
133

134
        for (int i = 0; i < this.methods.length; i++) {
9✔
135
            methodSerializers[i] = getMethodSerializer(this.methods[i].getReturnType());
10✔
136
        }
137
    }
1✔
138

139
    private void introspectWriteReplace(Class<?> cl, ClassLoader loader) {
140
        try {
141
            String className = cl.getName() + "HessianSerializer";
4✔
142

143
            Class<?> serializerClass = Class.forName(className, false, loader);
×
144

145
            Object serializerObject = serializerClass.getDeclaredConstructor().newInstance();
×
146

147
            Method method = getWriteReplaceWithParam(serializerClass, cl);
×
148

149
            if (method != null) {
×
150
                writeReplaceFactory = serializerObject;
×
151
                this.writeReplace = method;
×
152
                return;
×
153
            }
154
        } catch (ClassNotFoundException ignored) {
1✔
155
        } catch (Exception e) {
×
156
            log.trace(e.toString(), e);
×
157
        }
1✔
158

159
        writeReplace = getWriteReplaceForBean(cl);
5✔
160
    }
1✔
161

162
    /**
163
     * Use shared method from AbstractSerializer - delegates to base class
164
     */
165
    protected Method getWriteReplaceForBean(Class<?> cl) {
166
        return AbstractSerializer.getWriteReplace(cl);
3✔
167
    }
168

169
    /**
170
     * Use shared method from AbstractSerializer - delegates to base class
171
     */
172
    protected Method getWriteReplaceWithParam(Class<?> cl, Class<?> param) {
173
        return AbstractSerializer.getWriteReplace(cl, param);
×
174
    }
175

176
    @Override
177
    public void writeObject(Object obj, AbstractHessianEncoder out) throws IOException {
178
        if (out.addRef(obj)) {
4!
179
            return;
×
180
        }
181

182
        Class<?> cl = obj.getClass();
3✔
183

184
        try {
185
            if (writeReplace != null) {
3!
186
                Object repl;
187

188
                if (writeReplaceFactory != null) {
×
189
                    repl = writeReplace.invoke(writeReplaceFactory, obj);
×
190
                } else {
191
                    repl = writeReplace.invoke(obj);
×
192
                }
193

194
                out.writeObject(repl);
×
195

196
                out.replaceRef(repl, obj);
×
197

198
                return;
×
199
            }
200
        } catch (Exception e) {
×
201
            log.trace(e.toString(), e);
×
202
        }
1✔
203

204
        int ref = out.writeObjectBegin(cl.getName());
5✔
205

206
        if (ref == -1) {
3!
207
            out.writeInt(names.length);
5✔
208

209
            for (String name : names) {
17✔
210
                out.writeString(name);
3✔
211
            }
212

213
            out.writeObjectBegin(cl.getName());
5✔
214
        }
215

216
        for (int i = 0; i < methods.length; i++) {
9✔
217
            Method method = methods[i];
5✔
218
            methodSerializers[i].serialize(out, obj, method);
8✔
219
        }
220
    }
1✔
221

222
    /**
223
     * Finds any matching setter.
224
     */
225
    private boolean existsSetter(Method[] methods, String getterName, Class<?> arg) {
226
        String setterName = "set" + (getterName.startsWith("is") ? getterName.substring(2) : getterName.substring(3));
13✔
227

228
        for (Method method : methods) {
16✔
229
            if (method.getName().equals(setterName)) {
5✔
230
                Class<?>[] params = method.getParameterTypes();
3✔
231
                return params.length == 1 && params[0].equals(arg);
13!
232
            } else if ("readResolve".equals(method.getName()) && method.getParameterTypes().length == 0) {
5!
233
                return true;
×
234
            }
235
        }
236

237
        return false;
2✔
238
    }
239

240
    static class MethodNameCmp implements Comparator<Method> {
3✔
241
        @Override
242
        public int compare(Method a, Method b) {
243
            return a.getName().compareTo(b.getName());
6✔
244
        }
245
    }
246
}
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