J'essaie de jouer avec CompletableFuture
y Async
dans Spring Boot.
J'ai un service qui renvoie essentiellement un String
objet. La méthode du service a @Cacheable
sur lui, ce qui le fait servir à partir du cache Redis s'il est disponible.
J'ai le code suivant pour cela :
@Override
@Cacheable(value = "url-single", key = "#shortUrlKey", unless = "#result == null")
public Optional<String> retrieveOriginalUrl(String shortUrlKey) {
LOG.info("Finding original URL with Short URL Key: {}", shortUrlKey);
Optional<URLEntity> urlEntityOptional = urlRepository.findOneByShortUrlKey(shortUrlKey);
if (urlEntityOptional.isPresent()) {
LOG.info("Retrieved URL Entity: {}", urlEntityOptional.get());
return Optional.of(urlEntityOptional.get().getOriginalUrl());
}
LOG.info("No URL Entity Retrieved");
return Optional.empty();
}
Le code ci-dessus fonctionne bien lorsqu'il n'y a pas de CompletableFuture
impliqué.
Mais il se bloque lorsque la méthode est modifiée comme ci-dessous :
@Async
@Override
@Cacheable(value = "url-single", key = "#shortUrlKey", unless = "#result == null")
public CompletableFuture<Optional<String>> retrieveOriginalUrl(String shortUrlKey) {
LOG.info("Finding original URL with Short URL Key: {}", shortUrlKey);
Optional<URLEntity> urlEntityOptional = urlRepository.findOneByShortUrlKey(shortUrlKey);
if (urlEntityOptional.isPresent()) {
LOG.info("Retrieved URL Entity: {}", urlEntityOptional.get());
return CompletableFuture.completedFuture(Optional.of(urlEntityOptional.get().getOriginalUrl()));
}
LOG.info("No URL Entity Retrieved");
return CompletableFuture.completedFuture(Optional.empty());
}
J'utilise Redisson pour la mise en cache. L'erreur suivante se produit lorsque la méthode en question est appelée
java.lang.IllegalArgumentException: java.io.IOException: java.lang.RuntimeException: Class java.util.concurrent.CompletableFuture does not implement Serializable or externalizable
at org.redisson.RedissonObject.encodeMapValue(RedissonObject.java:326) ~[redisson-3.12.1.jar!/:3.12.1]
at org.redisson.RedissonMap.fastPutOperationAsync(RedissonMap.java:931) ~[redisson-3.12.1.jar!/:3.12.1]
at org.redisson.RedissonMap.fastPutAsync(RedissonMap.java:922) ~[redisson-3.12.1.jar!/:3.12.1]
at org.redisson.RedissonMap.fastPut(RedissonMap.java:936) ~[redisson-3.12.1.jar!/:3.12.1]
at org.redisson.spring.cache.RedissonCache.put(RedissonCache.java:109) ~[redisson-3.12.1.jar!/:3.12.1]
at org.springframework.cache.interceptor.AbstractCacheInvoker.doPut(AbstractCacheInvoker.java:87) ~[spring-context-5.2.2.RELEASE.jar!/:5.2.2.RELEASE]
at org.springframework.cache.interceptor.CacheAspectSupport$CachePutRequest.apply(CacheAspectSupport.java:820) ~[spring-context-5.2.2.RELEASE.jar!/:5.2.2.RELEASE]
at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:429) ~[spring-context-5.2.2.RELEASE.jar!/:5.2.2.RELEASE]
at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:345) ~[spring-context-5.2.2.RELEASE.jar!/:5.2.2.RELEASE]
at org.springframework.cache.interceptor.CacheInterceptor.invoke(CacheInterceptor.java:61) ~[spring-context-5.2.2.RELEASE.jar!/:5.2.2.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.2.2.RELEASE.jar!/:5.2.2.RELEASE]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212) ~[spring-aop-5.2.2.RELEASE.jar!/:5.2.2.RELEASE]
at com.sun.proxy.$Proxy92.retrieveOriginalUrlAsync(Unknown Source) ~[na:na]
at in.turls.lib.controllers.v1.TestAsync.redirect(TestAsync.java:75) ~[classes!/:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:567) ~[na:na]
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190) ~[spring-web-5.2.2.RELEASE.jar!/:5.2.2.RELEASE]
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138) ~[spring-web-5.2.2.RELEASE.jar!/:5.2.2.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106) ~[spring-webmvc-5.2.2.RELEASE.jar!/:5.2.2.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:888) ~[spring-webmvc-5.2.2.RELEASE.jar!/:5.2.2.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:793) ~[spring-webmvc-5.2.2.RELEASE.jar!/:5.2.2.RELEASE]
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.2.2.RELEASE.jar!/:5.2.2.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040) ~[spring-webmvc-5.2.2.RELEASE.jar!/:5.2.2.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943) ~[spring-webmvc-5.2.2.RELEASE.jar!/:5.2.2.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.2.2.RELEASE.jar!/:5.2.2.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) ~[spring-webmvc-5.2.2.RELEASE.jar!/:5.2.2.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:634) ~[tomcat-embed-core-9.0.29.jar!/:9.0.29]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.2.2.RELEASE.jar!/:5.2.2.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:741) ~[tomcat-embed-core-9.0.29.jar!/:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) ~[tomcat-embed-core-9.0.29.jar!/:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.29.jar!/:9.0.29]
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[tomcat-embed-websocket-9.0.29.jar!/:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.29.jar!/:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.29.jar!/:9.0.29]
at in.turls.lib.filters.RequestResponseLoggingFilter.doFilter(RequestResponseLoggingFilter.java:28) ~[classes!/:na]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.29.jar!/:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.29.jar!/:9.0.29]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.2.2.RELEASE.jar!/:5.2.2.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.2.RELEASE.jar!/:5.2.2.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.29.jar!/:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.29.jar!/:9.0.29]
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.2.2.RELEASE.jar!/:5.2.2.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.2.RELEASE.jar!/:5.2.2.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.29.jar!/:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.29.jar!/:9.0.29]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.2.2.RELEASE.jar!/:5.2.2.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.2.RELEASE.jar!/:5.2.2.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.29.jar!/:9.0.29]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.29.jar!/:9.0.29]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) ~[tomcat-embed-core-9.0.29.jar!/:9.0.29]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) ~[tomcat-embed-core-9.0.29.jar!/:9.0.29]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:526) ~[tomcat-embed-core-9.0.29.jar!/:9.0.29]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139) ~[tomcat-embed-core-9.0.29.jar!/:9.0.29]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) ~[tomcat-embed-core-9.0.29.jar!/:9.0.29]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) ~[tomcat-embed-core-9.0.29.jar!/:9.0.29]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) ~[tomcat-embed-core-9.0.29.jar!/:9.0.29]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:367) ~[tomcat-embed-core-9.0.29.jar!/:9.0.29]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) ~[tomcat-embed-core-9.0.29.jar!/:9.0.29]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:860) ~[tomcat-embed-core-9.0.29.jar!/:9.0.29]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1591) ~[tomcat-embed-core-9.0.29.jar!/:9.0.29]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) ~[tomcat-embed-core-9.0.29.jar!/:9.0.29]
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[na:na]
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[na:na]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-9.0.29.jar!/:9.0.29]
at java.base/java.lang.Thread.run(Thread.java:830) ~[na:na]
Caused by: java.io.IOException: java.lang.RuntimeException: Class java.util.concurrent.CompletableFuture does not implement Serializable or externalizable
at org.redisson.codec.FstCodec$2.encode(FstCodec.java:279) ~[redisson-3.12.1.jar!/:3.12.1]
at org.redisson.RedissonObject.encodeMapValue(RedissonObject.java:324) ~[redisson-3.12.1.jar!/:3.12.1]
... 66 common frames omitted
Caused by: java.lang.RuntimeException: Class java.util.concurrent.CompletableFuture does not implement Serializable or externalizable
at org.nustaq.serialization.FSTClazzInfo.<init>(FSTClazzInfo.java:144) ~[fst-2.57.jar!/:na]
at org.nustaq.serialization.FSTClazzInfoRegistry.getCLInfo(FSTClazzInfoRegistry.java:129) ~[fst-2.57.jar!/:na]
at org.nustaq.serialization.FSTObjectOutput.getFstClazzInfo(FSTObjectOutput.java:534) ~[fst-2.57.jar!/:na]
at org.nustaq.serialization.FSTObjectOutput.writeObjectWithContext(FSTObjectOutput.java:416) ~[fst-2.57.jar!/:na]
at org.nustaq.serialization.FSTObjectOutput.writeObjectInternal(FSTObjectOutput.java:327) ~[fst-2.57.jar!/:na]
at org.nustaq.serialization.FSTObjectOutput.writeObject(FSTObjectOutput.java:294) ~[fst-2.57.jar!/:na]
at org.nustaq.serialization.FSTObjectOutput.writeObject(FSTObjectOutput.java:204) ~[fst-2.57.jar!/:na]
at org.redisson.codec.FstCodec$2.encode(FstCodec.java:271) ~[redisson-3.12.1.jar!/:3.12.1]
... 67 common frames omitted
Quand j'utilise Optional
le cache de Spring est capable de stocker uniquement sa valeur, mais lorsque l'on utilise l'option CompletableFuture
il ne le fait pas. Considérant le fait que la méthode doit retourner un CompletableFuture
seulement, quelles sont mes options ici ?