Skip to content
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

Spring Session and Dev Tools Cause ClassCastException #3805

Closed
rwinch opened this issue Aug 21, 2015 · 11 comments
Closed

Spring Session and Dev Tools Cause ClassCastException #3805

rwinch opened this issue Aug 21, 2015 · 11 comments
Assignees
Milestone

Comments

@rwinch
Copy link
Member

rwinch commented Aug 21, 2015

If Spring Session Redis and Spring Boot Dev Tools are being used and a user defined class is persisted into HttpSession a ClassCastException will occur when trying to load it.

The problem is that Spring Session uses Spring Data Redis to load the class on each request. Spring Data Redis eventually uses DefaultDeserializer which uses an ObjectInputStream to load the class from the system ClassLoader.

This means that the value restored from session has a class defined by the system ClassLoader. The application itself loads the same class from Spring Boot's RestartClassLoader. Since the two classes are loaded from different ClassLoaders they cannot be cast from one to the other.

One possible solution is for Spring Redis to use a mechanism to customize the ObjectInputStream ClassLoader to use the ThreadLocal.getThread().getContextClassLoader(). For example, it could use ConfigurableObjectInputStream

I believe one alternative is that if DefaultDeserializer were loaded using RestartClassLoader, then the ObjectInputStream should use the RestartClassLoader.

Related (possibly replaces this issue): SPR-13409

@snicoll
Copy link
Member

snicoll commented Aug 22, 2015

Similar problem with other caches whose content survive the application refresh.

@rwinch
Copy link
Member Author

rwinch commented Aug 30, 2015

@snicoll Would SPR-13409 help with cache too?

@snicoll
Copy link
Member

snicoll commented Aug 31, 2015

probably not. Each cache implementation has its own way of deserializing cached data but I guess we could maybe tune the config when devtools runs. I am not sure if that's a huge problem though, persistent caching in dev mode seems like a lousy option.

We had an issue with our SpringOne demo that I need to dig a bit more. I'll create a separate issue if necessary. Thanks!

@philwebb
Copy link
Member

Unfortunately I've not found a way to work around this type of issue without making changes in the things that call ObjectInputStream. For now we're probably going to need to leave this as a known limitation.

@xoned1
Copy link

xoned1 commented Nov 30, 2015

So if you want Redis, it's not possible to use the dev-tools anymore? I'm facing the same error, which took me hours to find out (spring beginner).

@wilkinsona
Copy link
Member

So if you want Redis, it's not possible to use the dev-tools anymore

As things stand you can't use Spring Session backed by Redis and DevTools together.

I'm facing the same error, which took me hours to find out (spring beginner).

I'm sorry that you spent hours finding this out. It's certainly not the experience that we want beginners to have. This is mentioned in the known limitations section of the documentation, but perhaps you didn't see the description or it didn't mean much too you?

I'm wondering if we would be better failing fast when we detect the Spring Session, Redis, and DevTools combination with a message explaining that it won't work.

@wilkinsona wilkinsona added the for: team-attention An issue we'd like other members of the team to review label Nov 30, 2015
@xoned1
Copy link

xoned1 commented Nov 30, 2015

Thanks for your reply. Yes, you're right. The documentation was the answer, but to be honest, if you start with spring there is so much you take notice from and try to remember. I don't think it's a doc update worth.

@rwinch
Copy link
Member Author

rwinch commented Nov 30, 2015

This can be resolved for Spring Session and Spring Data Redis once DATAREDIS-427 is resolved. Perhaps a bump to @christophstrobl could make that happen?

A workaround for this when using Spring Session 1.1.0.M1+ can be found in spring-state-securing-restful-apis. Using the latest GA is slightly more complex to ensure the custom RedisSerializer is used. Something like the following should work:

@Configuration
class SpringSessionConfig {

    @Bean
    public RedisSerializer<Object> defaultRedisSerializer() {
        return new BeanClassLoaderAwareJdkRedisSerializer();
    }

    @Autowired
    public void configureSessionRedisTemplate(RedisTemplate<String,ExpiringSession> sessionRedisTemplate, RedisSerializer<Object> serializer) {
        sessionRedisTemplate.setDefaultSerializer(serializer);
    }

    static class BeanClassLoaderAwareJdkRedisSerializer implements RedisSerializer<Object>, BeanClassLoaderAware {
        private Converter<Object, byte[]> serializer = new SerializingConverter();
        private Converter<byte[], Object> deserializer;

        @Override
        public void setBeanClassLoader(ClassLoader classLoader) {
            this.deserializer = new DeserializingConverter(new DefaultDeserializer(classLoader));
        }

        public Object deserialize(byte[] bytes) {
            if (ObjectUtils.isEmpty(bytes)) {
                return null;
            }

            try {
                Object result = deserializer.convert(bytes);
                return result;
            } catch (Exception ex) {
                throw new SerializationException("Cannot deserialize", ex);
            }
        }

        public byte[] serialize(Object object) {
            if (object == null) {
                return new byte[0];
            }
            try {
                return serializer.convert(object);
            } catch (Exception ex) {
                throw new SerializationException("Cannot serialize", ex);
            }
        }

    }
}

@wilkinsona
Copy link
Member

While we're waiting on some changes in Spring Data Redis and Spring Session, we could probably auto-configure this workaround in DevTools.

@philwebb philwebb modified the milestones: 1.3.1, Backlog Dec 2, 2015
@philwebb philwebb removed the for: team-attention An issue we'd like other members of the team to review label Dec 2, 2015
@wilkinsona wilkinsona assigned wilkinsona and unassigned philwebb Dec 4, 2015
@eagle8625
Copy link

I refer to this link to resolve my problem: http://stackoverflow.com/questions/2591779/cast-across-classloader, however I don't know why one class is read into ObjectOutputStream then written into ObjectInputStream can change its classloader.

@raysuliteanu
Copy link

FYI same problem with Apache Ignite; add an object to an Ignite cache in the restartedMain and get from cache in another thread (embedded Tomcat nio thread) with launcher classloader and get a CCE. Don't use devtools and problem goes away. This is on Boot 1.5.4 and Ignite 2.0.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants