11

I am working on a project that generates Java files. I'd like to be able to optionally add the serialVersionUID as you would with the serialver tool.

Is there a way to do this when I generate the Java code, or will I need to ask the user of the tool to provide UIDs manually? To be clear, I'm not looking to do this automatically through Eclipse or the serialver tool, but to do it via Java itself.

asked Aug 17, 2013 at 23:37
4
  • Maybe the hashcode of the class name of the class being generated would work? Commented Aug 17, 2013 at 23:39
  • I was thinking about something like that, wondering if there is a way to do it that is equivalent to the serialver tool though, if possible. Commented Aug 17, 2013 at 23:41
  • 1
    Sure, you can do it like the serialver tool does it. Just find the logic behind its serialVersionUID generation. I've been looking with no success so far. Commented Aug 17, 2013 at 23:43
  • Found what you were looking for @bn. Check my answer. Commented Aug 17, 2013 at 23:46

5 Answers 5

12

There is a version of the serialver tool source available from OpenJDK. It all comes down to this call:

ObjectStreamClass c = ObjectStreamClass.lookup(MyClass.class);
long serialID = c.getSerialVersionUID();
System.out.println(serialID);

In JDK 6 at least it returns the same number with serialver tool.

answered Aug 18, 2013 at 0:03
Sign up to request clarification or add additional context in comments.

1 Comment

I tried this and the result was that it didn't detect new changes made in the class when deserializing from an old version. It seems that you always get a 0 from method getSerialVersionUID(), so the serial ID never changes along with the class. It seems to me that you can't set that ID programatically.
5

From ObjectStreamClass:

/**
 * Computes the default serial version UID value for the given class.
 */
private static long computeDefaultSUID(Class<?> cl) {
 if (!Serializable.class.isAssignableFrom(cl) || Proxy.isProxyClass(cl))
 {
 return 0L;
 }
 try {
 ByteArrayOutputStream bout = new ByteArrayOutputStream();
 DataOutputStream dout = new DataOutputStream(bout);
 dout.writeUTF(cl.getName());
 int classMods = cl.getModifiers() &
 (Modifier.PUBLIC | Modifier.FINAL |
 Modifier.INTERFACE | Modifier.ABSTRACT);
 /*
 * compensate for javac bug in which ABSTRACT bit was set for an
 * interface only if the interface declared methods
 */
 Method[] methods = cl.getDeclaredMethods();
 if ((classMods & Modifier.INTERFACE) != 0) {
 classMods = (methods.length > 0) ?
 (classMods | Modifier.ABSTRACT) :
 (classMods & ~Modifier.ABSTRACT);
 }
 dout.writeInt(classMods);
 if (!cl.isArray()) {
 /*
 * compensate for change in 1.2FCS in which
 * Class.getInterfaces() was modified to return Cloneable and
 * Serializable for array classes.
 */
 Class<?>[] interfaces = cl.getInterfaces();
 String[] ifaceNames = new String[interfaces.length];
 for (int i = 0; i < interfaces.length; i++) {
 ifaceNames[i] = interfaces[i].getName();
 }
 Arrays.sort(ifaceNames);
 for (int i = 0; i < ifaceNames.length; i++) {
 dout.writeUTF(ifaceNames[i]);
 }
 }
 Field[] fields = cl.getDeclaredFields();
 MemberSignature[] fieldSigs = new MemberSignature[fields.length];
 for (int i = 0; i < fields.length; i++) {
 fieldSigs[i] = new MemberSignature(fields[i]);
 }
 Arrays.sort(fieldSigs, new Comparator<MemberSignature>() {
 public int compare(MemberSignature ms1, MemberSignature ms2) {
 return ms1.name.compareTo(ms2.name);
 }
 });
 for (int i = 0; i < fieldSigs.length; i++) {
 MemberSignature sig = fieldSigs[i];
 int mods = sig.member.getModifiers() &
 (Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED |
 Modifier.STATIC | Modifier.FINAL | Modifier.VOLATILE |
 Modifier.TRANSIENT);
 if (((mods & Modifier.PRIVATE) == 0) ||
 ((mods & (Modifier.STATIC | Modifier.TRANSIENT)) == 0))
 {
 dout.writeUTF(sig.name);
 dout.writeInt(mods);
 dout.writeUTF(sig.signature);
 }
 }
 if (hasStaticInitializer(cl)) {
 dout.writeUTF("<clinit>");
 dout.writeInt(Modifier.STATIC);
 dout.writeUTF("()V");
 }
 Constructor[] cons = cl.getDeclaredConstructors();
 MemberSignature[] consSigs = new MemberSignature[cons.length];
 for (int i = 0; i < cons.length; i++) {
 consSigs[i] = new MemberSignature(cons[i]);
 }
 Arrays.sort(consSigs, new Comparator<MemberSignature>() {
 public int compare(MemberSignature ms1, MemberSignature ms2) {
 return ms1.signature.compareTo(ms2.signature);
 }
 });
 for (int i = 0; i < consSigs.length; i++) {
 MemberSignature sig = consSigs[i];
 int mods = sig.member.getModifiers() &
 (Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED |
 Modifier.STATIC | Modifier.FINAL |
 Modifier.SYNCHRONIZED | Modifier.NATIVE |
 Modifier.ABSTRACT | Modifier.STRICT);
 if ((mods & Modifier.PRIVATE) == 0) {
 dout.writeUTF("<init>");
 dout.writeInt(mods);
 dout.writeUTF(sig.signature.replace('/', '.'));
 }
 }
 MemberSignature[] methSigs = new MemberSignature[methods.length];
 for (int i = 0; i < methods.length; i++) {
 methSigs[i] = new MemberSignature(methods[i]);
 }
 Arrays.sort(methSigs, new Comparator<MemberSignature>() {
 public int compare(MemberSignature ms1, MemberSignature ms2) {
 int comp = ms1.name.compareTo(ms2.name);
 if (comp == 0) {
 comp = ms1.signature.compareTo(ms2.signature);
 }
 return comp;
 }
 });
 for (int i = 0; i < methSigs.length; i++) {
 MemberSignature sig = methSigs[i];
 int mods = sig.member.getModifiers() &
 (Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED |
 Modifier.STATIC | Modifier.FINAL |
 Modifier.SYNCHRONIZED | Modifier.NATIVE |
 Modifier.ABSTRACT | Modifier.STRICT);
 if ((mods & Modifier.PRIVATE) == 0) {
 dout.writeUTF(sig.name);
 dout.writeInt(mods);
 dout.writeUTF(sig.signature.replace('/', '.'));
 }
 }
 dout.flush();
 MessageDigest md = MessageDigest.getInstance("SHA");
 byte[] hashBytes = md.digest(bout.toByteArray());
 long hash = 0;
 for (int i = Math.min(hashBytes.length, 8) - 1; i >= 0; i--) {
 hash = (hash << 8) | (hashBytes[i] & 0xFF);
 }
 return hash;
 } catch (IOException ex) {
 throw new InternalError();
 } catch (NoSuchAlgorithmException ex) {
 throw new SecurityException(ex.getMessage());
 }
}
answered Aug 18, 2013 at 0:11

2 Comments

@Jeffrey... Quite Crafty +1
Doesn't look like a copy-pasteable code. Where is the hasStaticInitializer() method declared? Where is the MemberSignature class declared?
4

If your tool is generating brand new code you don't have any need to compute it the way serialver does. Just use 1, or -1, or whatever you like.

answered Aug 19, 2013 at 0:05

2 Comments

Could you explain why? Is this because it is a new class?
@bn. As it's a new class, there are no existing serializations of it, so there is nothing to be compatible to.
3

Try the hashcode of the class name of the class being generated.

There could be collisions as a hashcode is not unique, but those collisions are statistically unlikely.

Here's documentation on how serialVersionUID values are generated. Its much more complex than I'd have guessed.

Because of its complexity, I'd either have the user type in the UID themselves, or just use a simple hash of the full classname.

answered Aug 17, 2013 at 23:41

Comments

3

This is an old thread but, I guess, serialVersionUID is still one of the hot topics.

When I started to write Java codes, it was too hard to find and assign a unique value to serlialVersionUID variable for me. After a while, simple date time format (yyyy-MM-ddTHH:mm:ss) gave me the idea: Why don't I generate a value from current date and time?

So, I started to generate (of course manually) values according to current date and time.

Let's say current date and time is 01/09/2015 11:00pm. I would assign 201601091100L value to serialVersionUID.

I hope this idea helps you for further improvements in your projects.

answered Jan 9, 2016 at 21:17

2 Comments

By the way, that format for date-time strings is defined by the very useful ISO 8601 standard. The hyphens and colons are optional as you have shown, called the "basic" variation. The java.time framework built into Java 8 and later uses ISO 8601 formats by default in parsing/generating string representations of date-time values.
You don't need to assign a unique number. You can use 1 or -1.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.