Skip to content

SerialVersionUID on class MessageHistory Breaking change : from version 5.5.2 to latest #3737

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
german22 opened this issue Mar 4, 2022 · 7 comments · Fixed by #3738
Closed
Labels
ideal-for-user-contribution An issue that would ideal for a user to get started with contributing. in: core type: bug
Milestone

Comments

@german22
Copy link

german22 commented Mar 4, 2022

In what version(s) of Spring Integration are you seeing this issue?

After the version 5.5.2 until now

bug

The Class MessageHistory is a serializable class that doesn't have a SerialVersionUID defined, because of that it was taken the default SerialVersionUID created with the previous version.

Now this class was updated in the commit :
af9e69c

Where the equals/hashCode was implemented
Because of that, after the version 5.5.2 the serialVersionUID changed

This change of serialVersionUID makes the previos version incompatible with the new one, because the serialVersionUID matching fails when it tries to deserialize the class.

To Reproduce

Use spring integration with the version 5.5.1 to store a new record, and then update the version to the 5.5.2 or latest to read this record, and it will fails

Expected behavior

Deserialize the class MessageHistory stored in previous versions to the 5.5.2

Is it possible to fix this problem ?
Is there any workaround that we can implement in the meantime ?

Thanks a lot

@german22 german22 added status: waiting-for-triage The issue need to be evaluated and its future decided type: bug labels Mar 4, 2022
@german22 german22 changed the title SerialVersionUID on class MessageHistory Breaking change version 5.5.1 to latest SerialVersionUID on class MessageHistory Breaking change : from version 5.5.2 to latest Mar 4, 2022
@artembilan artembilan added this to the 6.0 M2 milestone Mar 4, 2022
@artembilan artembilan added backport 5.5.x in: core and removed status: waiting-for-triage The issue need to be evaluated and its future decided labels Mar 4, 2022
@artembilan
Copy link
Member

We can and an explicit serialVersionUID as a fix, but I guess it won't help you.

As a workaround I only see a way to copy/paste a 5.5.1 MessageHistory class to your project or separate jar and make it to be loaded before spring-integration-core.

WDYT?

@artembilan artembilan added the ideal-for-user-contribution An issue that would ideal for a user to get started with contributing. label Mar 4, 2022
@onobc
Copy link
Contributor

onobc commented Mar 5, 2022

I think we can set the explicit serialVersionUID to the value it had in 5.5.1. We can use the serialver tool to tell us what that value is.

Thoughts?

@artembilan
Copy link
Member

Thanks, @onobc !

I think this is brilliant.

Here is a result of that command for me:

C:\Java\jdk-8\bin>serialver.exe -classpath c:\spring-integration-core-5.5.1.jar;c:\spring-messaging-5.3.8.jar;c:\spring-jcl-5.3.8.jar org.springframework.integration.history.MessageHistory
org.springframework.integration.history.MessageHistory:    private static final long serialVersionUID = 1426799817181873282L;

@german22 ,

would you mind to confirm from your side?
if that is the same, then we indeed can use that value for the current 5.5.10 version we are going to release on March 22th.

onobc added a commit to onobc/spring-integration that referenced this issue Mar 6, 2022
onobc added a commit to onobc/spring-integration that referenced this issue Mar 6, 2022
onobc added a commit to onobc/spring-integration that referenced this issue Mar 6, 2022
@onobc
Copy link
Contributor

onobc commented Mar 7, 2022

@german22 you could also try to run w/ PR branch to verify.

@german22
Copy link
Author

german22 commented Mar 7, 2022

Thanks, @onobc !

I think this is brilliant.

Here is a result of that command for me:

C:\Java\jdk-8\bin>serialver.exe -classpath c:\spring-integration-core-5.5.1.jar;c:\spring-messaging-5.3.8.jar;c:\spring-jcl-5.3.8.jar org.springframework.integration.history.MessageHistory
org.springframework.integration.history.MessageHistory:    private static final long serialVersionUID = 1426799817181873282L;

@german22 ,

would you mind to confirm from your side? if that is the same, then we indeed can use that value for the current 5.5.10 version we are going to release on March 22th.

Hi,

Thanks a lot for your help.
I confirm: this is the exact same value 1426799817181873282

@artembilan
Copy link
Member

@onobc ,

here is the value for the current class:

C:\Java\jdk-17\bin>serialver.exe -classpath c:\spring-integration-core-6.0.0-SNAPSHOT.jar;c:\spring-messaging-6.0.0-M2.jar;c:\spring-jcl-6.0.0-M2.jar org.springframework.integration.history.MessageHistory
org.springframework.integration.history.MessageHistory:    private static final long serialVersionUID = -2340400235574314134L;

onobc added a commit to onobc/spring-integration that referenced this issue Mar 8, 2022
onobc added a commit to onobc/spring-integration that referenced this issue Mar 8, 2022
onobc added a commit to onobc/spring-integration that referenced this issue Mar 8, 2022
onobc added a commit to onobc/spring-integration that referenced this issue Mar 8, 2022
onobc added a commit to onobc/spring-integration that referenced this issue Mar 8, 2022
artembilan pushed a commit that referenced this issue Mar 8, 2022
Fixes #3737

* Add an explicit `serialVersionUID` to `MessageHistory` to avoid class version conflicts in the future.

See related GH issue for the workaround

**Cherry-pick to `5.5.x`**
artembilan pushed a commit that referenced this issue Mar 8, 2022
Fixes #3737

* Add an explicit `serialVersionUID` to `MessageHistory` to avoid class version conflicts in the future.

See related GH issue for the workaround

**Cherry-pick to `5.5.x`**
@onobc
Copy link
Contributor

onobc commented Mar 8, 2022

@german22

Here is the code sample for the suggested workaround. It is a custom deserializer that allows a configurable set of "alternative" serialVersionUID to be used during deserialization. In this case we will want to configure it to allow the 5.5.1 UIDs for MessageHistory (we actually can skip the nested MessageHistory.Entry because it's UID has not changed).

The one annoyance of the workaround is that you have to set the deserializer on whatever store you are using that is currently failing to deser the objects.

Class UID (5.5.1) UID (5.5.2+)
MessageHistory 1426799817181873282L 2340400235574314134L
MessageHistory.Entry -8225834391885601079L -8225834391885601079L

Usage

This assumes a JDBC message store is being used (adjust accordingly).

VersionTolerantJavaDeserializer deser = new VersionTolerantJavaDeserializer();
deser.allowSerialVersionUID(MessageHistory.class, 1426799817181873282L);
JdbcMessageStore messageStore = ...
messageStore.setDeserializer(deser);
Message<MessageHistory> oldMsg = messageStore.getMessage(oldMsgId);

Workaround

ℹ️ While this is a "sample" I did write tests and verified functionality before sharing:

import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectStreamClass;
import java.io.Serializable;

import org.springframework.core.NestedIOException;
import org.springframework.core.serializer.Deserializer;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

/**
 * A {@link Deserializer} implementation that reads an input stream using Java serialization in a manner that
 * allows {@link Serializable} classes to be deserialized with more than one {@code serialVersionUID}.
 *
 * @author  Chris Bono
 */
public class VersionTolerantJavaDeserializer implements Deserializer<Object> {

	private final MultiValueMap<Class<?>, Long> alternativeSerialVersionUIDs = new LinkedMultiValueMap<>();

	/**
	 * Allows an alternative {@code serialVersionUID} to be used when deserializing a class.
	 * @param clazz the class
	 * @param serialVersionUID the alternate uid to allow
	 */
	public void allowSerialVersionUID(Class<?> clazz, long serialVersionUID) {
		this.alternativeSerialVersionUIDs.add(clazz, serialVersionUID);
	}

	@Override
	public Object deserialize(InputStream inputStream) throws IOException {
		VersionTolerantObjectInputStream objectInputStream = new VersionTolerantObjectInputStream(inputStream);
		try {
			return objectInputStream.readObject();
		}
		catch (ClassNotFoundException ex) {
			throw new NestedIOException("Failed to deserialize object type", ex);
		}
	}

	private final class VersionTolerantObjectInputStream extends ObjectInputStream {

		private VersionTolerantObjectInputStream(InputStream in) throws IOException {
			super(in);
		}

		@Override
		protected ObjectStreamClass readClassDescriptor() throws IOException, ClassNotFoundException {
			ObjectStreamClass resultClassDescriptor = super.readClassDescriptor();

			String className = resultClassDescriptor.getName();
			Class<?> localClass = Class.forName(className);
			if (!alternativeSerialVersionUIDs().containsKey(localClass)) {
				return resultClassDescriptor;
			}

			ObjectStreamClass localClassDescriptor = ObjectStreamClass.lookup(localClass);
			if (localClassDescriptor == null) {
				return resultClassDescriptor;
			}

			final long localSerialVersionUID = localClassDescriptor.getSerialVersionUID();
			final long streamSerialVersionUID = resultClassDescriptor.getSerialVersionUID();
			if (streamSerialVersionUID != localSerialVersionUID && alternativeSerialVersionUIDs().get(localClass)
					.contains(streamSerialVersionUID)) {
				return localClassDescriptor;
			}

			return resultClassDescriptor;
		}

		private MultiValueMap<Class<?>, Long> alternativeSerialVersionUIDs() {
			return VersionTolerantJavaDeserializer.this.alternativeSerialVersionUIDs;
		}
	}
}

Let us know how it goes @german22

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
ideal-for-user-contribution An issue that would ideal for a user to get started with contributing. in: core type: bug
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants