I. Overview
Recently, an enumeration class has been split, but it is used on an RPC interface. When an enumeration class is used on an RPC interface, serialization and deserialization must be considered. You need to ensure that your own splitting of the enumeration does not result in serialization and deserialization.
The original code was
public enum xxxEnum { X("a11","a22"), Y("a111","a222"); private String a1; private String a2; public String getA1(){ return a1;} public String getA2(){ return a2;} }
The modified code is
public enum xxxEnum { X("b11","b22"), Y("b111","b222"); private String b1; private String b2; public String getB1(){ return b1;} public String getB2(){ return b2;} }
The names of two member variables were changed.Will it be a problem to modify deserialization like this?
2. Problem Analysis
To see if serialization and deserialization are okay, first look at the source code for serialization and deserialization. There's a lot about serialization and deserialization of hessian. Here's an enumeration point to analyze.
First look at the code for enumeration serialization
public class EnumSerializer extends AbstractSerializer { private Method _name; public EnumSerializer(Class cl) { // hessian/32b[12], hessian/3ab[23] if (!cl.isEnum() && cl.getSuperclass().isEnum()) cl = cl.getSuperclass(); try { // name method for enumeration classes by reflection _name = cl.getMethod("name", new Class[0]); } catch (Exception e) { throw new RuntimeException(e); } } public void writeObject(Object obj, AbstractHessianOutput out) throws IOException { if (out.addRef(obj)) return; Class cl = obj.getClass(); if (!cl.isEnum() && cl.getSuperclass().isEnum()) cl = cl.getSuperclass(); String name = null; try { //Invoke the name method of an enumeration class to generate enumeration serialized values name = (String) _name.invoke(obj, (Object[]) null); } catch (Exception e) { throw new RuntimeException(e); } out.writeMapBegin(cl.getName()); out.writeString("name"); out.writeString(name); out.writeMapEnd(); } }
Serialization of enumerations can be summarized as calling the name method of an enumeration class to generate a serialized string.(
Take another look at the code for enumerating deserialization
public class EnumDeserializer extends AbstractDeserializer { private Class _enumType; private Method _valueOf; public EnumDeserializer(Class cl) { // hessian/33b[34], hessian/3bb[78] if (cl.isEnum()) _enumType = cl; else if (cl.getSuperclass().isEnum()) _enumType = cl.getSuperclass(); else throw new RuntimeException("Class " + cl.getName() + " is not an enum"); try { // Reflect the valueOf method to get an enumeration class _valueOf = _enumType.getMethod("valueOf", new Class[] { Class.class, String.class }); } catch (Exception e) { throw new RuntimeException(e); } } public Class getType() { return _enumType; } public Object readMap(AbstractHessianInput in) throws IOException { String name = null; while (! in.isEnd()) { String key = in.readString(); if (key.equals("name")) name = in.readString(); else in.readObject(); } in.readMapEnd(); Object obj = create(name); in.addRef(obj); return obj; } @Override public Object readObject(AbstractHessianInput in, Object []fields) throws IOException { String []fieldNames = (String []) fields; String name = null; for (int i = 0; i < fieldNames.length; i++) { if ("name".equals(fieldNames[i])) name = in.readString(); else in.readObject(); } Object obj = create(name); in.addRef(obj); return obj; } private Object create(String name) throws IOException { if (name == null) throw new IOException(_enumType.getName() + " expects name."); try { //Reflection calls the valueOf method of an enumeration class return _valueOf.invoke(null, _enumType, name); } catch (Exception e) { throw new IOExceptionWrapper(e); } } }
Deserialization of enumerations can be summarized as reflection calling the enumeration's valueOf method to get the final enumeration value.
With the above analysis of the source code for enumeration serialization deserialization, now let's look at the relevant issues.(Assuming the server does serialization, the client does deserialization for easy description)
1. The server side enumeration has one more enumeration value
If the server-side enumeration class is
public enum A { X, Y, Z; }
The client's enumeration classes are
public enum A { X, Y; }
If the server returns an A.Z to the client, then hessian deserialization calls the valueOf method of the enumeration class to get the deserialization, but the client does not have Z in the enumeration class, then the client deserialization runs out of order.
2. Service side enumeration ordinal value and enumeration class member variable value are inconsistent with client side
Assume the server-side enumeration class is
public enum A { X("aaa"), Y("bbbb");//Y has ordinal 1 and corresponding value bbb String value; A(String value) {this.value=value} }
The client's enumeration classes are
public enum A { X("aaa"), Z("ccc"), Y("ddd");// In this case, the ordinal of Y is 2 and the corresponding value is ddd String value; A(String value) {this.value=value;} }
If the incoming server passes A.Y to the client, then the client gets A.Y with ordinal 2 and value ddd.(
This point above is very important.(
3. Enumerations are singular
public enum TestEnum { XX("xx"); TestEnum(String value) { this.value = value; } String value; public String getValue() { return value; } public void setValue(String value) { this.value = value; } } public class Test { public static void main(String[] rgs) { TestEnum testEnum1 = TestEnum.XX; TestEnum testEnum2 = TestEnum.XX; testEnum1.setValue("XX"); testEnum2.setValue("YY"); System.out.println(testEnum1.value); // Output YY System.out.println(testEnum2.value); // Output YY } }
TesEnum1 and testEnum2 actually point to the same enumeration reference.The same object is modified each time, so the value of the previous set is overridden by the subsequent set.
3. Summary
- Or don't use enumeration classes directly in RPC interfaces, just String
- Use name() directly when using strings in enumerated classes, do not overwrap, and try to keep enumerated classes concise
- Be careful when using enumeration classes on RPC interfaces, and be careful to keep ordinal when refactoring
- Enumeration takes nothing but name when serializing and deserializing
- Prohibit set ting methods for enumerations, useless
I. Overview
Recently, an enumeration class has been split, but it is used on an RPC interface. When an enumeration class is used on an RPC interface, serialization and deserialization must be considered. You need to ensure that your own splitting of the enumeration does not result in serialization and deserialization.