Method Annotated with #EventHandler is not getting triggered in Axon Based Spring Boot Microservice - spring-boot

The event is getting stored in the event store but not getting persisted in the Entity table.
My Controller method
#PostMapping("/saveProduct")
public ResponseEntity<Void> productCreator(#RequestBody CreateProductModel createProductModel){
CreateProductCommand createProductCommand = CreateProductCommand.builder().
productId(UUID.randomUUID().toString()).
title(createProductModel.getTitle()).
description(createProductModel.getDescription()).
price(createProductModel.getPrice()).
discountPercentage(createProductModel.getDiscountPercentage()).
available(createProductModel.isAvailable()).build();
try {
commandGateway.sendAndWait(createProductCommand);
}catch(Exception e) {
e.printStackTrace();
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
return new ResponseEntity<>(HttpStatus.OK);
}
Aggregate Class
#Aggregate
public class ProductAggregate {
#AggregateIdentifier
private String productId;
private String title;
private String description;
private int price;
private short discountPercentage;
private boolean available;
public ProductAggregate() {
//leave it blank always
}
#CommandHandler
public ProductAggregate(CreateProductCommand createProductCommand) {
ProductCreatedEvent productCreatedEvent = new ProductCreatedEvent();
BeanUtils.copyProperties(createProductCommand, productCreatedEvent);
AggregateLifecycle.apply(productCreatedEvent);
}
#EventSourcingHandler
public void on(ProductCreatedEvent productCreatedEvent) {
this.productId = productCreatedEvent.getProductId();
this.title = productCreatedEvent.getTitle();
this.description = productCreatedEvent.getDescription();
this.price = productCreatedEvent.getPrice();
this.discountPercentage = productCreatedEvent.getDiscountPercentage();
this.available = productCreatedEvent.isAvailable();
}
}
But this below class's method public void on(ProductCreatedEvent productCreatedEvent) is not getting called because of which the events are getting stored in event store but not getting persisted. Can someone help why this is happening?
#Service
public class ProductEventsHandler {
private final ProductRepo productRepo;
public ProductEventsHandler(ProductRepo productRepo) {
this.productRepo = productRepo;
}
#EventHandler
public void on(ProductCreatedEvent productCreatedEvent) {
//1. Create object of entity
Product product = new Product();
//2. Copy from productCreatedEvent(event) to entity
BeanUtils.copyProperties(productCreatedEvent, product);
//3. repository.save(entity);
productRepo.save(product);
}
}
Also, I am getting an exception while running the application as below :
2021-11-07 00:19:20.677 INFO 6668 --- [ main] com.netflix.discovery.DiscoveryClient : Initializing Eureka in region us-east-1
2021-11-07 00:19:20.719 INFO 6668 --- [ main] c.n.d.s.r.aws.ConfigClusterResolver : Resolving eureka endpoints via configuration
2021-11-07 00:19:20.782 WARN 6668 --- [s.CQRS.query]-0] o.a.e.TrackingEventProcessor : Error occurred. Starting retry mode.
com.thoughtworks.xstream.security.ForbiddenClassException: com.solinvictus.Products.CQRS.events.ProductCreatedEvent
at com.thoughtworks.xstream.security.NoTypePermission.allows(NoTypePermission.java:26) ~[xstream-1.4.18.jar:1.4.18]
at com.thoughtworks.xstream.mapper.SecurityMapper.realClass(SecurityMapper.java:74) ~[xstream-1.4.18.jar:1.4.18]
at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:125) ~[xstream-1.4.18.jar:1.4.18]
at com.thoughtworks.xstream.mapper.CachingMapper.realClass(CachingMapper.java:47) ~[xstream-1.4.18.jar:1.4.18]
at org.axonframework.serialization.AbstractXStreamSerializer.classForType(AbstractXStreamSerializer.java:159) ~[axon-messaging-4.2.jar:4.2]
at org.axonframework.serialization.LazyDeserializingObject.<init>(LazyDeserializingObject.java:83) ~[axon-messaging-4.2.jar:4.2]
at org.axonframework.eventhandling.EventUtils.lambda$upcastAndDeserializeTrackedEvents$1(EventUtils.java:99) ~[axon-messaging-4.2.jar:4.2]
at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193) ~[na:1.8.0_202]
at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193) ~[na:1.8.0_202]
at org.axonframework.eventsourcing.eventstore.BatchingEventStorageEngine$EventStreamSpliterator.tryAdvance(BatchingEventStorageEngine.java:242) ~[axon-eventsourcing-4.2.jar:4.2]
at java.util.stream.StreamSpliterators$WrappingSpliterator.lambda$initPartialTraversalState$0(StreamSpliterators.java:294) ~[na:1.8.0_202]
at java.util.stream.StreamSpliterators$AbstractWrappingSpliterator.fillBuffer(StreamSpliterators.java:206) ~[na:1.8.0_202]
at java.util.stream.StreamSpliterators$AbstractWrappingSpliterator.doAdvance(StreamSpliterators.java:161) ~[na:1.8.0_202]
at java.util.stream.StreamSpliterators$WrappingSpliterator.tryAdvance(StreamSpliterators.java:300) ~[na:1.8.0_202]
at java.util.Spliterators$1Adapter.hasNext(Spliterators.java:681) ~[na:1.8.0_202]
at org.axonframework.eventsourcing.eventstore.EmbeddedEventStore$EventConsumer.peekPrivateStream(EmbeddedEventStore.java:397) ~[axon-eventsourcing-4.2.jar:4.2]
at org.axonframework.eventsourcing.eventstore.EmbeddedEventStore$EventConsumer.peek(EmbeddedEventStore.java:356) ~[axon-eventsourcing-4.2.jar:4.2]
at org.axonframework.eventsourcing.eventstore.EmbeddedEventStore$EventConsumer.hasNextAvailable(EmbeddedEventStore.java:333) ~[axon-eventsourcing-4.2.jar:4.2]
at org.axonframework.common.stream.BlockingStream.hasNextAvailable(BlockingStream.java:40) ~[axon-messaging-4.2.jar:4.2]
at org.axonframework.eventhandling.TrackingEventProcessor.checkSegmentCaughtUp(TrackingEventProcessor.java:464) ~[axon-messaging-4.2.jar:4.2]
at org.axonframework.eventhandling.TrackingEventProcessor.processBatch(TrackingEventProcessor.java:351) ~[axon-messaging-4.2.jar:4.2]
at org.axonframework.eventhandling.TrackingEventProcessor.processingLoop(TrackingEventProcessor.java:275) ~[axon-messaging-4.2.jar:4.2]
at org.axonframework.eventhandling.TrackingEventProcessor$TrackingSegmentWorker.run(TrackingEventProcessor.java:1071) [axon-messaging-4.2.jar:4.2]
at org.axonframework.eventhandling.TrackingEventProcessor$WorkerLauncher.run(TrackingEventProcessor.java:1183) [axon-messaging-4.2.jar:4.2]
at java.lang.Thread.run(Thread.java:748) [na:1.8.0_202]

You've encountered a failure in Axon's upcaster because of a ForbiddenClassException thrown by XStream.
2021-11-07 00:19:20.782 WARN 6668 --- [s.CQRS.query]-0] o.a.e.TrackingEventProcessor : Error occurred. Starting retry mode.
com.thoughtworks.xstream.security.ForbiddenClassException: com.solinvictus.Products.CQRS.events.ProductCreatedEvent
To solve this you should allow XStream for your event classes to be serialized as stated in this answer

There were two issues that I found in my code :
The issue was with XStream so I excluded the Xstream dependency from the axon framework and added the XStream dependency myself ().
<dependency>
<groupId>org.axonframework</groupId>
<artifactId>axon-spring-boot-starter</artifactId>
<version>4.2</version>
<exclusions>
<exclusion>
<groupId>org.axonframework</groupId>
<artifactId>axon-server-connector</artifactId>
</exclusion>
<exclusion>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.4.17</version>
</dependency>
The silly one:
I had annotated the "id" field of the entity class with #GeneratedValue and since I was populating the value using UUID that was causing the error. It was causing the error. I didn't notice this issue until I did the 1st fix(mentioned above)

Related

Spring-boot 2.7.0 with jax-ws 1.9 WSServletContextListener Unable to start embedded Tomcat

What do I do wrong with JAX-WS and SPRING-BOOT?
I use jaxws-spring v1.9, spring-boot-starter-parent v2.7.0.
I don`t have web.xml.
I have #WebService() #SOAPBinding()
My problem is org.springframework.boot.web.server.WebServerException: Unable to start embedded Tomcat
but file is in /src/main/resource/WEB-INF/sun-jaxws.xml
2022-08-04 16:47:58.026 ERROR 20076 --- [ main] o.s.boot.SpringApplication : Application run failed
org.springframework.context.ApplicationContextException: Unable to start web server; nested exception is org.springframework.boot.web.server.WebServerException: Unable to start embedded Tomcat
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:165) ~[spring-boot-2.7.0.jar:2.7.0]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:577) ~[spring-context-5.3.20.jar:5.3.20]
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:147) ~[spring-boot-2.7.0.jar:2.7.0]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:734) ~[spring-boot-2.7.0.jar:2.7.0]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:408) ~[spring-boot-2.7.0.jar:2.7.0]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:308) ~[spring-boot-2.7.0.jar:2.7.0]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1306) ~[spring-boot-2.7.0.jar:2.7.0]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1295) ~[spring-boot-2.7.0.jar:2.7.0]
at com.thecloud.telecom.TelecomApplication.main(TelecomApplication.java:14) ~[classes/:na]
Caused by: org.springframework.boot.web.server.WebServerException: Unable to start embedded Tomcat
at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.initialize(TomcatWebServer.java:142) ~[spring-boot-2.7.0.jar:2.7.0]
at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.<init>(TomcatWebServer.java:104) ~[spring-boot-2.7.0.jar:2.7.0]
at org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory.getTomcatWebServer(TomcatServletWebServerFactory.java:479) ~[spring-boot-2.7.0.jar:2.7.0]
at org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory.getWebServer(TomcatServletWebServerFactory.java:211) ~[spring-boot-2.7.0.jar:2.7.0]
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.createWebServer(ServletWebServerApplicationContext.java:184) ~[spring-boot-2.7.0.jar:2.7.0]
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:162) ~[spring-boot-2.7.0.jar:2.7.0]
... 8 common frames omitted
Caused by: java.lang.IllegalStateException: StandardEngine[Tomcat].StandardHost[localhost].TomcatEmbeddedContext[] failed to start
at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.rethrowDeferredStartupExceptions(TomcatWebServer.java:187) ~[spring-boot-2.7.0.jar:2.7.0]
at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.initialize(TomcatWebServer.java:126) ~[spring-boot-2.7.0.jar:2.7.0]
... 13 common frames omitted
Disconnected from the target VM, address: '127.0.0.1:52298', transport: 'socket'
Process finished with exit code 1
It happens when I try to use
JaxWsListener
import javax.servlet.ServletContextAttributeEvent;
import javax.servlet.ServletContextAttributeListener;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import com.sun.xml.ws.transport.http.servlet.WSServletContextListener;
#WebListener
public class JaxWsListener implements ServletContextAttributeListener, ServletContextListener {
private final WSServletContextListener listener;
public JaxWsListener() {
this.listener = new WSServletContextListener();
}
#Override
public void attributeAdded(ServletContextAttributeEvent event) {
listener.attributeAdded(event);
}
#Override
public void attributeRemoved(ServletContextAttributeEvent event) {
listener.attributeRemoved(event);
}
#Override
public void attributeReplaced(ServletContextAttributeEvent event) {
listener.attributeReplaced(event);
}
#Override
public void contextInitialized(ServletContextEvent sce) {
listener.contextInitialized(sce);
}
#Override
public void contextDestroyed(ServletContextEvent sce) {
listener.contextDestroyed(sce);
}
}
without this Listener spring-boot (tomcat) started well.
for JAX-WS as well I use this class :
JaxWsServlet
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.sun.xml.ws.transport.http.servlet.WSServlet;
import lombok.extern.log4j.Log4j2;
#Log4j2
#WebServlet(name = "JaxWsServlet", urlPatterns = JaxWsServlet.URL_PATTERN, loadOnStartup = 1)
public class JaxWsServlet extends WSServlet {
public static final String URL_PATTERN = "/jaxws";
public JaxWsServlet() {
LOG.debug("CREATE {}", this.getClass().getSimpleName());
}
#Override
public void init(ServletConfig servletConfig) throws ServletException {
super.init(servletConfig);
LOG.debug("JAX-WS Init");
}
#Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException {
super.doPost(request, response);
LOG.debug("JAX-WS DoPost");
}
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException {
super.doGet(request, response);
LOG.debug("JAX-WS DoGet");
}
}
as well I have /WEB-INF/soap-jaxws.xml
<endpoints xmlns="http://java.sun.com/xml/ns/jax-ws/ri/runtime"
version="2.0">
<endpoint name="SoapService"
implementation="com.ws.endpoint.SoapService"
url-pattern="/ws/call1" />
<endpoint name="SoapService2"
implementation="com.ws.endpoint.SoapService2"
url-pattern="/ws/call2" />
</endpoints>
this file parsed in the WSServletContextListener - it is hard coded there.
I solved this issue by putting /WEB-INF/sun-jaxws.xml into **/srv/main/resources/META-INF/resources/**WEB-INF/sun-jaxrs.xml
this helped me
It is enough to have in your spring-boot application:
/WEB-INF/sun-jaxws.xml - BCS it is HARDCODED in the jax-ws
#WebService
#WebListener
spring-boot-starter-parent:2.7.0
org.jvnet.jax-ws-commons.spring:jaxws-spring:1.9 - with exclusions
#Service
#WebService(
name = "SoapService",
endpointInterface = "com.ws.service.SoapService")
#SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE)
public class SoapService
implements ISoapService { ....
and #WebListener (look at it above)
#WebListener
public class JaxWsListener implements ServletContextAttributeListener, ServletContextListener {
private final WSServletContextListener listener;
........
You do not need #WebServlet
pom.xml:
<dependency>
<groupId>org.jvnet.jax-ws-commons.spring</groupId>
<artifactId>jaxws-spring</artifactId>
<version>1.9</version>
<exclusions>
<exclusion>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
</exclusion>
<exclusion>
<groupId>com.sun.xml.ws</groupId>
<artifactId>jaxws-rt</artifactId>
</exclusion>
<!-- ? -->
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</exclusion>
<exclusion>
<groupId>com.sun.xml.stream.buffer</groupId>
<artifactId>streambuffer</artifactId>
</exclusion>
<exclusion>
<groupId>org.jvnet.staxex</groupId>
<artifactId>stax-ex</artifactId>
</exclusion>
</exclusions>
</dependency>

Use ElasticSearch client in non web application with Spring

I'm creating a non web application with Spring that will read data from Kafka and write it in ElasticSearch, i included the following dependency:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
And the following configuration class:
#Configuration
public class ElasticSearchConfig extends AbstractElasticsearchConfiguration{
#Bean
public RestHighLevelClient elasticsearchClient() {
final ClientConfiguration clientConfiguration = ClientConfiguration.builder().connectedTo("localhost:9200")
.build();
return RestClients.create(clientConfiguration).rest();
}
}
When i execute the following code:
#Override
public void run(String... args) throws Exception {
IndexRequest request = new IndexRequest("test-transactions");
request.id("2");
request.source("{\"name\":\"Sammie\"," + "\"lastname\":\"Goldner\"," + "\"username\":\"hugh.vonrueden\","
+ "\"amount\":9622235.2009}", XContentType.JSON);
client.index(request, RequestOptions.DEFAULT);
}
I get the following exception:
Caused by: java.lang.ClassNotFoundException: org.springframework.http.HttpHeaders
at java.net.URLClassLoader.findClass(URLClassLoader.java:382) ~[na:1.8.0_251]
at java.lang.ClassLoader.loadClass(ClassLoader.java:418) ~[na:1.8.0_251]
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:355) ~[na:1.8.0_251]
at java.lang.ClassLoader.loadClass(ClassLoader.java:351) ~[na:1.8.0_251]
... 46 common frames omitted
If i include the following dependency the code works fine:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
But it brings a lot of other functionalities that i don't want in my code, is there any other way to configure spring-boot-starter-data-elasticsearch in a non web application?
Thanks.

Spring Boot test tries to initialize cache2k for the 2nd time and fails

After adding cache2k to my project some #SpringBootTest's stopped working with an error:
java.lang.IllegalStateException: Cache already created: 'cache'
Below I provide the minimal example to reproduce:
Go to start.spring.io and create a simplest Maven project with Cache starter, then add cache2k dependencies:
<properties>
<java.version>1.8</java.version>
<cache2k-version>1.2.2.Final</cache2k-version>
</properties>
<dependencies>
<dependency>
<groupId>org.cache2k</groupId>
<artifactId>cache2k-api</artifactId>
<version>${cache2k-version}</version>
</dependency>
<dependency>
<groupId>org.cache2k</groupId>
<artifactId>cache2k-core</artifactId>
<version>${cache2k-version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.cache2k</groupId>
<artifactId>cache2k-spring</artifactId>
<version>${cache2k-version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
Now configure the simplest cache:
#SpringBootApplication
#EnableCaching
public class CachingDemoApplication {
public static void main(String[] args) {
SpringApplication.run(CachingDemoApplication.class, args);
}
#Bean
public CacheManager springCacheManager() {
SpringCache2kCacheManager cacheManager = new SpringCache2kCacheManager();
cacheManager.addCaches(b -> b.name("cache"));
return cacheManager;
}
}
And add any service (which we will #MockBean in one of our tests:
#Service
public class SomeService {
public String getString() {
System.out.println("Executing service method");
return "foo";
}
}
Now two #SpringBootTest tests are required to reproduce the issue:
#SpringBootTest
#RunWith(SpringRunner.class)
public class SpringBootAppTest {
#Test
public void getString() {
System.out.println("Empty test");
}
}
#RunWith(SpringRunner.class)
#SpringBootTest
public class WithMockedBeanTest {
#MockBean
SomeService service;
#Test
public void contextLoads() {
}
}
Notice that the 2nd test has mocked #MockBean. This causes an error (stacktrace below).
Caused by: java.lang.IllegalStateException: Cache already created: 'cache'
at org.cache2k.core.CacheManagerImpl.newCache(CacheManagerImpl.java:174)
at org.cache2k.core.InternalCache2kBuilder.buildAsIs(InternalCache2kBuilder.java:239)
at org.cache2k.core.InternalCache2kBuilder.build(InternalCache2kBuilder.java:182)
at org.cache2k.core.Cache2kCoreProviderImpl.createCache(Cache2kCoreProviderImpl.java:215)
at org.cache2k.Cache2kBuilder.build(Cache2kBuilder.java:837)
at org.cache2k.extra.spring.SpringCache2kCacheManager.buildAndWrap(SpringCache2kCacheManager.java:205)
at org.cache2k.extra.spring.SpringCache2kCacheManager.lambda$addCache$2(SpringCache2kCacheManager.java:143)
at java.util.concurrent.ConcurrentHashMap.compute(ConcurrentHashMap.java:1853)
at org.cache2k.extra.spring.SpringCache2kCacheManager.addCache(SpringCache2kCacheManager.java:141)
at org.cache2k.extra.spring.SpringCache2kCacheManager.addCaches(SpringCache2kCacheManager.java:132)
at com.example.cachingdemo.CachingDemoApplication.springCacheManager(CachingDemoApplication.java:23)
at com.example.cachingdemo.CachingDemoApplication$$EnhancerBySpringCGLIB$$2dce99ca.CGLIB$springCacheManager$0(<generated>)
at com.example.cachingdemo.CachingDemoApplication$$EnhancerBySpringCGLIB$$2dce99ca$$FastClassBySpringCGLIB$$bbd240c0.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:244)
at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:363)
at com.example.cachingdemo.CachingDemoApplication$$EnhancerBySpringCGLIB$$2dce99ca.springCacheManager(<generated>)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154)
... 52 more
If you remove #MockBean, both tests will pass.
How can I avoid this error in my test suite?
Your second test represents a different ApplicationContext altogether so the test framework will initiate a dedicated one for it. If cache2k is stateful (for instance sharing the CacheManager for a given classloader if it already exists), the second context will attempt to create a new CacheManager while the first one is still active.
You either need to flag one of the test as dirty (see #DirtiesContext) which will close the context and shut down the CacheManager, or you can replace the cache infrastructure by an option that does not require all that, see #AutoConfigureCache.
If cache2k works in such a way that it requires you to dirty the context, I'd highly recommend to swap it using the later options.
Since I do not want any custom behavior in test, but just want to get rid of this error, the solution is to create CacheManager using unique name like this:
#Bean
public CacheManager springCacheManager() {
SpringCache2kCacheManager cacheManager = new SpringCache2kCacheManager("spring-" + hashCode());
cacheManager.addCaches(b -> b.name("cache"));
return cacheManager;
}
I encountered the same error when using cache2k with Spring Dev Tools, and ended up with the following code as the solution:
#Bean
public CacheManager cacheManager() {
SpringCache2kCacheManager cacheManager = new SpringCache2kCacheManager();
// To avoid the "Caused by: java.lang.IllegalStateException: Cache already created:"
// error when Spring DevTools is enabled and code reloaded
if (cacheManager.getCacheNames().stream()
.filter(name -> name.equals("cache"))
.count() == 0) {
cacheManager.addCaches(
b -> b.name("cache")
);
}
return cacheManager;
}

Problems setting up Spring form validation

I have some problems setting up the validation in Spring MVC (4.3.9) application.
Basically, there is a simple application, with the issue isolated to one "contact" form.
Here is part of the code of the bean underlying the form:
#Component
#Scope("request")
public class SendMail {
#Min(20)
private String userName;
#Size(min=10, max=11)
private String message;
private String userEmail;
public static class CustomValidator implements Validator {
#Override
public boolean supports(Class clazz) {
return SendMail.class.equals(clazz);
}
#Override
public void validate(Object targets, Errors errors) {
SendMail o = (SendMail) targets;
if (o.getUserName() == null || o.getUserName().length() < 1) {
errors.rejectValue("userName", "Empty.userName");
}
}
}
....
}
and here is the part of the relevant controller:
#RequestMapping(value = {"/contact"}, method = RequestMethod.POST)
public ModelAndView sendEmail(#Valid SendMail sendmail, BindingResult result) {
System.out.println("Result1: " + result.hasErrors());
DataBinder binder = new DataBinder(sendmail);
binder.setValidator(new SendMail.CustomValidator());
binder.validate();
result = binder.getBindingResult();
System.out.println("Result2: " + result.hasErrors());
I get the following result:
Result1: false
Result2: true
So, the 'general' (denoted by the #Valid annotation) validation doesnt seem to work, but a manually-invoked validation does.
I googled for some time and have come across suggestions that a validation bean needs to be defined in the spring config. Unfortunately, when I add the following to my Config.java:
....
#Bean(name = "validator")
public Validator getValidator() {
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
return validator;
}
The application crashes with the following exception:
....
root cause
java.lang.ClassNotFoundException: javax.el.ExpressionFactory
java.net.URLClassLoader.findClass(URLClassLoader.java:381)
java.lang.ClassLoader.loadClass(ClassLoader.java:424)
sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:335)
java.lang.ClassLoader.loadClass(ClassLoader.java:357)
java.lang.ClassLoader.defineClass1(Native Method)
java.lang.ClassLoader.defineClass(ClassLoader.java:763)
java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
java.net.URLClassLoader.defineClass(URLClassLoader.java:467)
java.net.URLClassLoader.access$100(URLClassLoader.java:73)
java.net.URLClassLoader$1.run(URLClassLoader.java:368)
java.net.URLClassLoader$1.run(URLClassLoader.java:362)
java.security.AccessController.doPrivileged(Native Method)
java.net.URLClassLoader.findClass(URLClassLoader.java:361)
java.lang.ClassLoader.loadClass(ClassLoader.java:424)
sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:335)
java.lang.ClassLoader.loadClass(ClassLoader.java:411)
java.lang.ClassLoader.loadClass(ClassLoader.java:357)
java.lang.Class.forName0(Native Method)
java.lang.Class.forName(Class.java:348)
org.apache.catalina.loader.WebappClassLoaderBase.loadClass(Unknown Source)
org.apache.catalina.loader.WebappClassLoaderBase.loadClass(Unknown Source)
javax.el.ExpressionFactory.newInstance(Unknown Source)
javax.el.ExpressionFactory.newInstance(Unknown Source)
....
This supposedly indicates, there is no lib with EL implementation. And here is where I'm stuck. I have tried a number of dependency (using maven) with no avail. Currently the relevant part of my pom looks like this:
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.el</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.4.1.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator-annotation-processor</artifactId>
<version>5.4.1.Final</version>
</dependency>
I have tried various set-ups, with el-impl 2.2, etc. addin el.jar to Tomcat clasppath. But no luck at all =(
What is even more starange, if you look at the exception above, the call actually goes through "javax.el.ExpressionFactory.newInstance(Unknown Source)" before resulting in "java.lang.ClassNotFoundException: javax.el.ExpressionFactory".
I'd be grateful for any hints on how to fix/debug the issue...
Add this dependency .
<dependency>
<groupId>org.glassfish.web</groupId>
<artifactId>el-impl</artifactId>
<version>2.2</version>
</dependency>
Follow solution here .
I have found the solution to my issue. Strange as it seems, when I added el.jar supplied with Tomcat to the classpath in catalina.sh, the validation works. It might be a distro-related issue (i'm running it on Gentoo).

Facebook instance not getting created in Spring Social

I am trying to run a simple Spring Social application to login to Facebook account. But, Facebook instance is not created and it is showing nullpointerexception.
#Controller
#RequestMapping("/fb")
public class HelloFBController {
#Autowired
private Facebook facebook;
#RequestMapping(method = RequestMethod.GET)
public String helloFacebook(Model model) {
if (!facebook.isAuthorized()) {
return "redirect:/connect/facebook";
}
model.addAttribute("feed", facebook.feedOperations().getFeed());
return "hellofb";
}
}
Then
#Configuration
public class SocialConfig {
#Bean
public ConnectionFactoryLocator connectionFactoryLocator() {
ConnectionFactoryRegistry registry = new ConnectionFactoryRegistry();
registry.addConnectionFactory(new FacebookConnectionFactory(
environment.getProperty("facebook.appId"),
environment.getProperty("facebook.appSecret")));
return registry;
}
#Inject
private Environment environment;
}
Then application.properties file
spring.social.facebook.appId=XXXXXXXXXXXX
spring.social.facebook.appSecret=XXXXXXXXXXXXXXXXXXX
Then pom.xml is
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.5.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-social-facebook</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
</dependencies>
Whether I am missing something. When I run this application using Spring Boot, I am getting the exception in Controller as bellow. I am trying to access the application at localhost:8080/fb
java.lang.NullPointerException: null
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:317)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
at org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:133)
at org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:121)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
at $Proxy42.isAuthorized(Unknown Source)
at net.javabeat.spring.social.web.HelloFBController.helloFacebook(HelloFBController.java:19)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvok
You are missing the facebook dependency, see below:
<dependency>
<groupId>org.springframework.social</groupId>
<artifactId>spring-social-facebook</artifactId>
</dependency>
Follow link below for more information:
Accessing Facebook Data
Do you have this project in a Git repository somewhere that I could try without having to recreate the example from the blog entry you pulled it from?
I also notice that your Maven dependencies are reference Spring Boot starters for Spring Social Facebook, but that you are explicitly configuring the connection factory locator (and nothing else). Consequently, if you are using Spring Boot, then your explicit configuration is probably disabled auto-configuration provided by Spring Boot. Delete SocialConfig and see what happens.
You can change the code for a workaround to make it working. At this time facebook is not connnected or injected so it is getting NPE:
try {
if (!facebook.isAuthorized()) {
return "redirect:/connect/facebook";
}
} catch (Exception e) {
return "redirect:/connect/facebook";
}
Also, you may have to add spring.social.auto_connection_views=true if you are not using custom view.

Resources