Spring-Boot Rest Controller on Payara 5 ignores JAXB annotations - java

I have a problem with Spring-Boot application that I want to deploy to Payara 5.
I have visited Spring Initializr page, I've filled group, artifact, and added Web dependency. To make it possible to deploy application to Payara, I've removed dependencies to Tomcat, I've adjusted #SpringBootApplication annotated class, to extend SpringBootServletInitializer. And I've created very simple RestController that returns very simple Pojo.
Here is the code:
pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.sample</groupId>
<artifactId>rest-payara</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>rest-glassfish</name>
<description>Demo project for Spring Boot</description>
<packaging>war</packaging>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.1.1.RELEASE</version>
<exclusions>
<exclusion>
<artifactId>spring-boot-starter-tomcat</artifactId>
<groupId>org.springframework.boot</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<artifactId>spring-boot-starter-tomcat</artifactId>
<groupId>org.springframework.boot</groupId>
<version>2.1.1.RELEASE</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>2.1.1.RELEASE</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Application class:
package com.sample.restpayara;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
#SpringBootApplication
public class RestPayaraApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(RestPayaraApplication.class, args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(RestPayaraApplication.class);
}
}
Rest controller:
package com.sample.restpayara;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class RestApiController {
#GetMapping(value = "/sample-pojo", produces = { MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE })
public SamplePojo getSamplePojo() {
return new SamplePojo("Sample pojo");
}
}
Pojo:
package com.sample.restpayara;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name = "PojoRoot")
public class SamplePojo {
#XmlElement(name = "pojoContent")
private String content;
public SamplePojo() {
}
public SamplePojo(String content) {
this.content = content;
}
public String getContent() {
return content;
}
}
When I run this application with
mvn spring-boot:run
Everything works like I want, CURL request:
curl -k -i -X GET "http://localhost:8080/sample-pojo" -H "accept: application/xml" -H "Content-Type: application/xml"
Returns:
HTTP/1.1 200
Content-Type: application/xml
Transfer-Encoding: chunked
Date: Fri, 28 Dec 2018 09:26:08 GMT
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><PojoRoot><pojoContent>Sample pojo</pojoContent></PojoRoot>
However when I deploy this code to Payara and I do CURL request:
curl -k -i -X GET "https://my-payara-domain.local:8181/rest-payara-0.0.1-SNAPSHOT/sample-pojo" -H "accept: application/xml" -H "Content-Type: application/xml"
i receive response:
HTTP/2 200
content-type: application/xml;charset=UTF-8
<SamplePojo><content>Sample pojo</content></SamplePojo>
And here is the problem - why are JAXB annotations ignored on Payara and what do I have to do to make them work?

For anyone ever fighting with similar problem - the root cause of the issue was related to the fact, that MappingJackson2HttpMessageConverter was kicking in on Payara 5, while Jaxb2RootElementHttpMessageConverter was not there.
I found a solution for my problem by providing a configuration:
package com.sample.restpayara;
import java.util.List;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
#Configuration
public class JaxbSupportConfiguration extends WebMvcConfigurationSupport {
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new Jaxb2RootElementHttpMessageConverter());
converters.add(new MappingJackson2HttpMessageConverter());
}
}
Hope it helps someone in the future, for me it took 1,5 day to figure it out ;(
=================================
UPDATE: First solution did turn on Web MVC and caused static files not to be served anymore. I've managed to find a final solution by providing configuration:
package com.sample.restpayara;
import java.util.Arrays;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.context.annotation.Bean;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter;
#SpringBootApplication
public class RestPayaraApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(RestPayaraApplication.class, args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(RestPayaraApplication.class);
}
#Bean
public HttpMessageConverters converters() {
return new HttpMessageConverters(true, Arrays.asList(
new MappingJackson2HttpMessageConverter(),
new Jaxb2RootElementHttpMessageConverter())
);
}
}

Related

SpringBoot connection to Redis : Does not appear to be caching data

I am using SpringBoot to connect to Redis. I have Web dependency on SpringBoot and my intention is to write Product information to a runtime datastructure i.e., a Map. I want to test the Cache annotations that Spring provides to understand the usage.
Here is the POM.xml file
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>io.fireacademy</groupId>
<artifactId>redisconnectivity</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>redisconnectivity</name>
<description>Demo project for Spring Boot & Redis connectivity</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Here is the Spring Application class
package io.fireacademy.redisconnectivity;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
#SpringBootApplication
#EnableCaching
public class RedisconnectivityApplication {
public static void main(String[] args) {
SpringApplication.run(RedisconnectivityApplication.class, args);
}
}
Here is my main controller class
package io.fireacademy.redisconnectivity.controllers;
import io.fireacademy.redisconnectivity.model.Product;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
#RestController
#RequestMapping("/products")
public class WebAppController {
private static final Logger logger = LoggerFactory.getLogger(WebAppController.class);
// This will serve as the database
private Map<String, Product> m_productDatabase = new HashMap<String, Product>();
#Cacheable(value="my-product-cache", key="#productId")
private Product getProductFromCacheOrDB(String productId)
{
logger.info("Loading the Product " + productId + " from the cache!.");
return m_productDatabase.get(productId);
}
#CacheEvict(value="my-product-cache", key="#productId")
private Product deleteFromCache(String productId)
{
logger.info("Remove the Product " + productId + " from the cache!.");
return m_productDatabase.get(productId);
}
#GetMapping(path="/")
public ResponseEntity<List<Product>> getProducts()
{
Collection<Product> allProducts = m_productDatabase.values();
List<Product> allProductsAsList = allProducts.stream().collect(Collectors.toList());
return new ResponseEntity<List<Product>>(allProductsAsList, HttpStatus.OK);
}
#GetMapping(path="/{productId}")
public ResponseEntity<Product> getProducts(#PathVariable String productId)
{
// Either from the Cache or from the DB.
Product product = getProductFromCacheOrDB(productId);
return new ResponseEntity<Product>(product, HttpStatus.OK);
}
#PostMapping(consumes = {MediaType.APPLICATION_XML_VALUE,MediaType.APPLICATION_JSON_VALUE},
produces = {MediaType.APPLICATION_XML_VALUE,MediaType.APPLICATION_JSON_VALUE})
public ResponseEntity<Product> createProduct(#RequestBody Product product)
{
m_productDatabase.put(product.getId(), product);
return new ResponseEntity<Product>(product, HttpStatus.CREATED);
}
#PutMapping(path="/{productId}",
consumes = {MediaType.APPLICATION_XML_VALUE,MediaType.APPLICATION_JSON_VALUE},
produces = {MediaType.APPLICATION_XML_VALUE,MediaType.APPLICATION_JSON_VALUE})
public ResponseEntity<Product> updateProduct(#PathVariable String productId, #RequestBody Product product)
{
m_productDatabase.put(productId, product);
return new ResponseEntity<Product>(product, HttpStatus.OK);
}
#DeleteMapping(path="/{productId}")
public ResponseEntity<Product> deleteProduct(#PathVariable String productId)
{
Product deletedProduct = getProductFromCacheOrDB(productId);
deleteFromCache(productId);
return new ResponseEntity<Product>(deletedProduct, HttpStatus.OK);
}
}
Here is my RedisConfig class
package io.fireacademy.redisconnectivity.configurations;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.repository.configuration.EnableRedisRepositories;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
#Configuration
#EnableRedisRepositories
public class RedisConfig {
#Bean
public JedisConnectionFactory connectionFactory() {
RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration();
configuration.setHostName("localhost");
configuration.setPort(6379);
return new JedisConnectionFactory(configuration);
}
#Bean
public RedisTemplate<String, Object> template() {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory());
template.setKeySerializer(new StringRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashKeySerializer(new JdkSerializationRedisSerializer());
template.setValueSerializer(new JdkSerializationRedisSerializer());
template.setEnableTransactionSupport(true);
template.afterPropertiesSet();
return template;
}
}
My application.properties looks as below:
# Redis Config
spring.cache.type=redis
spring.redis.host=localhost
spring.redis.port=6379
And I am running Redis as a docker container using this:
docker run -d -p 6379:6379 --name my-redis redis
When I inspect the docker container's logs, I see nothing happening.
D:\Development\springboot\learn_redis>docker logs -f c376f1be9be35281b900c2943fbf8ea37e1563157efb57d46ca1c74fc880bc5c
1:C 04 Jun 2022 17:49:51.854 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
1:C 04 Jun 2022 17:49:51.854 # Redis version=7.0.0, bits=64, commit=00000000, modified=0, pid=1, just started
1:C 04 Jun 2022 17:49:51.854 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf
1:M 04 Jun 2022 17:49:51.854 * monotonic clock: POSIX clock_gettime
1:M 04 Jun 2022 17:49:51.858 * Running mode=standalone, port=6379.
1:M 04 Jun 2022 17:49:51.858 # Server initialized
1:M 04 Jun 2022 17:49:51.858 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.
1:M 04 Jun 2022 17:49:51.858 * The AOF directory appendonlydir doesn't exist
1:M 04 Jun 2022 17:49:51.858 * Ready to accept connections
My understanding was that the Cache annotations would make the data be stored onto the Redis cache. For instance, the getProductFromCacheOrDB, should store the Product object onto the cache with the input productId as the key because of the #Cacheable annotation and a subsequent call to get this specific Product using the GET productId should not invoke the method again. But this is not happening.
Please show some pointers on what I could have missed...
Thanks,
Pavan.
Edits:
I do not see anything on Redis getting created. I enabled monitoring on Redis via the redis-cli, but see nothing.
I tried removing the RedisConfig class and see no change.
Just created a simple working example at https://github.com/bsbodden/basic-caching-spring-data-redis
What you need:
POM dependencies:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
In the app class or configurer #EnableCaching:
#SpringBootApplication
#EnableCaching
public class DemoApplication {
In the app class RedisCacheManager bean:
#Bean
public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig() //
.prefixCacheNameWith(this.getClass().getPackageName() + ".") //
.entryTtl(Duration.ofHours(1)) //
.disableCachingNullValues();
return RedisCacheManager.builder(connectionFactory) //
.cacheDefaults(config) //
.build();
}
#Cacheable in your controller:
#GetMapping("/{id}")
#Cacheable("some-cache")
public SomeModel get(#PathVariable("id") String id) {
return repo.findById(id).orElse(SomeModel.of("nope"));
}

Basic Spring based WSSecurity client gets a null response

I've been following this tutorial to learn how to develop a basic spring client and server application using wssecurity (certificates).
The demo works beautifully, but i need to deploy my application on a wildfly server, so i had to change the example a bit in order to avoid the embedded tomcat, the changes are as follows:
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.memorynotfound.spring.ws</groupId>
<artifactId>ws-security-certificate-wss4j-security-interceptor</artifactId>
<version>1.0.0-SNAPSHOT</version>
<name>SPRING-WS - ${project.artifactId}</name>
<url>http://memorynotfound.com</url>
<packaging>war</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.6.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web-services</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.ws</groupId>
<artifactId>spring-ws-security</artifactId>
</dependency>
<dependency>
<groupId>org.apache.ws.security</groupId>
<artifactId>wss4j</artifactId>
<version>1.6.19</version>
</dependency>
<dependency>
<groupId>wsdl4j</groupId>
<artifactId>wsdl4j</artifactId>
<version>1.6.3</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>jaxb2-maven-plugin</artifactId>
<version>2.5.0</version>
<executions>
<execution>
<id>xjc</id>
<goals>
<goal>xjc</goal>
</goals>
</execution>
</executions>
<configuration>
<sources>
<source>src/main/resources/xsd</source>
</sources>
<generatePackage>com.memorynotfound.beer</generatePackage>
</configuration>
</plugin>
</plugins>
</build>
RunServer.java
package com.memorynotfound.server;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
#SpringBootApplication
public class RunServer extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(RunServer.class);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(applicationClass);
}
private static Class<RunServer> applicationClass = RunServer.class;
}
BeerEndpoint.java
package it.corvallis.soap.endpoint;
import com.memorynotfound.beer.Beer;
import com.memorynotfound.beer.GetBeerRequest;
import com.memorynotfound.beer.GetBeerResponse;
import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;
import org.springframework.ws.server.endpoint.annotation.RequestPayload;
import org.springframework.ws.server.endpoint.annotation.ResponsePayload;
import org.springframework.ws.soap.server.endpoint.annotation.SoapAction;
#Endpoint
public class BeerEndpoint {
public static final String NAMESPACE_URI = "http://memorynotfound.com/beer";
#PayloadRoot(namespace = NAMESPACE_URI, localPart = "getBeerRequest")
#SoapAction("http://localhost:8080/ws/beers")
#ResponsePayload
public GetBeerResponse getBeer(#RequestPayload GetBeerRequest request) {
GetBeerResponse beerResponse = new GetBeerResponse();
Beer beer = new Beer();
beer.setId(request.getId());
beer.setName("Beer name");
beerResponse.setBeer(beer);
return beerResponse;
}
}
I deployed the server application on my wildfly and i tried to call it using the same client application used in the tutorial. The service seems to answer correctly, the beerResponse object is correctly istantiated, but, as soon as the object is being sent back to the the client, i see it's NULL.
What am i missing?
Thanks in advance
Oh well, next time i'll read better. At the same link of my first post, a guy asked the same question in the comments, here is the solution: add a callBackHandler to the client configuration, and modify the securityInterceptor to use this handler.
SoapClientConfig.java
package com.memorynotfound.client;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.oxm.jaxb.Jaxb2Marshaller;
import org.springframework.stereotype.Component;
import org.springframework.ws.client.support.interceptor.ClientInterceptor;
import org.springframework.ws.soap.security.wss4j2.Wss4jSecurityInterceptor;
import org.springframework.ws.soap.security.wss4j2.callback.KeyStoreCallbackHandler;
import org.springframework.ws.soap.security.wss4j2.support.CryptoFactoryBean;
import java.io.IOException;
#Configuration
public class SoapClientConfig {
#Bean
public Wss4jSecurityInterceptor securityInterceptor() throws Exception {
Wss4jSecurityInterceptor securityInterceptor = new Wss4jSecurityInterceptor();
// set security actions
securityInterceptor.setSecurementActions("Timestamp Signature Encrypt");
// sign the request
securityInterceptor.setSecurementUsername("client");
securityInterceptor.setSecurementPassword("changeit");
securityInterceptor.setSecurementSignatureCrypto(getCryptoFactoryBean().getObject());
// encrypt the request
securityInterceptor.setSecurementEncryptionUser("server-public");
securityInterceptor.setSecurementEncryptionCrypto(getCryptoFactoryBean().getObject());
securityInterceptor.setSecurementEncryptionParts("{Content}{http://memorynotfound.com/beer}getBeerRequest");
// sign the response
securityInterceptor.setValidationActions("Signature Encrypt");
securityInterceptor.setValidationSignatureCrypto(getCryptoFactoryBean().getObject());
securityInterceptor.setValidationDecryptionCrypto(getCryptoFactoryBean().getObject());
securityInterceptor.setValidationCallbackHandler(securityCallbackHandler());
return securityInterceptor;
}
#Bean
public CryptoFactoryBean getCryptoFactoryBean() throws IOException {
CryptoFactoryBean cryptoFactoryBean = new CryptoFactoryBean();
cryptoFactoryBean.setKeyStorePassword("changeit");
cryptoFactoryBean.setKeyStoreLocation(new ClassPathResource("client.jks"));
return cryptoFactoryBean;
}
#Bean
public Jaxb2Marshaller getMarshaller(){
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setContextPath("com.memorynotfound.beer");
return marshaller;
}
#Bean
public BeerClient getBeerClient() throws Exception {
BeerClient beerClient = new BeerClient();
beerClient.setMarshaller(getMarshaller());
beerClient.setUnmarshaller(getMarshaller());
beerClient.setDefaultUri("http://localhost:8080/ws/beers");
ClientInterceptor[] interceptors = new ClientInterceptor[]{securityInterceptor()};
beerClient.setInterceptors(interceptors);
return beerClient;
}
#Bean
public KeyStoreCallbackHandler securityCallbackHandler(){
KeyStoreCallbackHandler callbackHandler = new KeyStoreCallbackHandler();
callbackHandler.setPrivateKeyPassword("changeit");
return callbackHandler;
}
}

Spring boot - configure context-path

I have configured application.properties‬ as explained here with :
server.servlet.context-path=/test
And in my #RestController I have a simple a simple #RequestMapping:
#RequestMapping(value = "/products", method = RequestMethod.GET)
But still when sending a request to http://localhost:8080/test/products I get tomcat - 404 response.
Full code:
package com.siemens;
import java.util.HashMap;
import java.util.Map;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class ProductServiceController {
private static Map<String, Product> productRepo = new HashMap<>();
static {
Product honey = new Product();
honey.setId("1");
honey.setName("Honey");
productRepo.put(honey.getId(), honey);
Product almond = new Product();
almond.setId("2");
almond.setName("Almond");
productRepo.put(almond.getId(), almond);
}
#RequestMapping(value = "/products", method = RequestMethod.GET)
public ResponseEntity<Object> getProduct() {
return new ResponseEntity<>(productRepo.values(), HttpStatus.OK);
}
}
pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>siemens</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>siemens</name>
<description>Demo project for siemens</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
#SpringBootApplication:
#SpringBootApplication
public class SiemensApplication {
public static void main(String[] args) {
SpringApplication.run(SiemensApplication.class, args);
}
}
Running on tomcat 9, if there is more code or config needed please comment below, thank you.
Any request should 'know' how to get to this controller. I would suggest trying this:
#RestController
#RequestMapping("psc")
public class ProductServiceController {
#RequestMapping(value = "products", method = RequestMethod.GET)
public ResponseEntity<Object> getProduct() {
return new ResponseEntity<>(productRepo.values(), HttpStatus.OK);
}
this way you can send a GET request, to the following url:
http://localhost:8080/test/psc/products
add the #RequestMapping annotation to specify the path to this class(controller)
get rid of the "/" on every #RequestMapping path value (just a cosmetic suggestion)

Spring-boot AOP not working with maven

I am trying to implement AOP in spring-boot with the help of custom annotation and Around advice. I am adding the annotation before a GET api but i am not able to catch the function in my Aspect.
pom.xml
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.0.7.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<scope>compile</scope>
<version>1.8.12</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.6.11</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>2.0.3.RELEASE</version>
</dependency>
HiveService.java
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.METHOD })
public #interface HiveService{
//public String name() default "";
}
Followup.java
#HiveService
#RequestMapping(value="rest/services/{pr
oduct}", method = RequestMethod.GET, produces = "application/json; charset=UTF-8")
public String getFollowupCategoryList(#PathVariable String product) {
LOGGER.info("[getFollowupCategoryList]: started again ");
}
HiveConsumer.java
#Aspect
#Component
public class HiveConsumer {
#Around("#annotation(com.abc.xyz.HiveService)")
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
Logger LOGGER = LoggerFactory.getLogger(com.abc.xyz.FollowupController.class);
LOGGER.info("Before");
Object proceed = joinPoint.proceed();
long executionTime = System.currentTimeMillis() - start;
LOGGER.info(joinPoint.getSignature() + " executed in " + executionTime + "ms");
return proceed;
}
}
Configuration file for Spring AOP
#SpringBootApplication
#RestController
#EnableAspectJAutoProxy(proxyTargetClass = true)
public class ActivitiRestApplication extends
SpringBootServletInitializer {
public static void main(String[] args) throws Exception {
SpringApplication.run(applicationClass, args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder app) {
return app.sources(applicationClass);
}
private static Class<ActivitiRestApplication> applicationClass = ActivitiRestApplication.class;
The application runs nicely if you
add dependency org.springframework.boot:spring-boot-starter-web:2.0.3.RELEASE to your POM,
remove the redundant org.aspectj:aspectjrt because it is a subset of org.aspectj:aspectjweaver (and you had specified a non-matching version umber anyway),
add #RestController to your class containing the annotated method,
make method getFollowupCategoryList(..) actually return a string (in your sample code it does not return anything, thus does not even compile).
Here is the fixed version of your code as a full MCVE, i.e. including package and class names. I changed the logging code to use System.out so as to minimise dependencies for the sake of this demo.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>de.scrum-master.stackoverflow</groupId>
<artifactId>spring-boot-51512380</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.0.7.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<scope>compile</scope>
<version>1.8.12</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>2.0.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.0.3.RELEASE</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
Update: Actually, you can even reduce your POM to this:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>de.scrum-master.stackoverflow</groupId>
<artifactId>spring-boot-51512380</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>2.0.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.0.3.RELEASE</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
package de.scrum_master.app;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.METHOD })
public #interface HiveService {
//public String name() default "";
}
package de.scrum_master.app;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class SomeClass {
#HiveService
#RequestMapping(value = "rest/services/{product}", method = RequestMethod.GET, produces = "application/json; charset=UTF-8")
public String getFollowupCategoryList(#PathVariable String product) {
System.out.println("[getFollowupCategoryList]: started again");
return "foo";
}
}
package de.scrum_master.app;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.web.bind.annotation.RestController;
#SpringBootApplication
#RestController
#EnableAspectJAutoProxy(proxyTargetClass = true)
public class ActivitiRestApplication extends SpringBootServletInitializer {
private static Class<ActivitiRestApplication> applicationClass = ActivitiRestApplication.class;
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder app) {
return app.sources(applicationClass);
}
public static void main(String[] args) throws Exception {
SpringApplication.run(applicationClass, args);
}
}
package de.scrum_master.app;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
#Aspect
#Component
public class HiveConsumer {
#Around("#annotation(de.scrum_master.app.HiveService)")
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
System.out.println("Before");
Object proceed = joinPoint.proceed();
long executionTime = System.currentTimeMillis() - start;
System.out.println(joinPoint.getSignature() + " executed in " + executionTime + "ms");
return proceed;
}
}
Now if you run the application and open URL http://localhost:8080/rest/services/something in a web browser, it will display foo in the browser and this on the console:
Before
[getFollowupCategoryList]: started again
String de.scrum_master.app.SomeClass.getFollowupCategoryList(String) executed in 2ms

Cannot Autowire DataSource in Spring Boot Project

I am trying to build a simple Spring Boot CRUD application that also has login and signup options with spring boot security. I'm already working with a MySQL database and its working fine to persist the data for my application.
The problem is that, in trying to create my jdbcAuthentication, in my securityConfig class, it says that I cannot autowire Datasource, and that there are no beans of 'DataSource' type found (again, I have used my MySQL database successfully for this project, for a while now). It also automatically imports the javax.sql.DataSource import when I type it in, so it does recognize it.
I tried to search through similar questions, but just could not get it to work.
Here is my code:
Test2Application.java
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class Test2Application {
public static void main(String[] args) {
SpringApplication.run(Test2Application.class, args);
}
}
SecurityConfig.java
package com.example.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.sql.DataSource;
#EnableWebSecurity
#RequestMapping("cheese")
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private DataSource dataSource;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication().dataSource(dataSource)
.usersByUsernameQuery("select email as principal, password as credentials, true from user where email=?");
}
#Override
protected void configure(HttpSecurity http) throws Exception{
http
.authorizeRequests()
.antMatchers(
"/cheese/index",
"/cheese/",
"/**/webjars/**",
"/cheese/signup",
"/cheese/login",
"/cheese/account",
"/cheese/add",
"/cheese/remove",
"/cheese/success").permitAll()
.anyRequest().authenticated()
.and()
.formLogin().loginPage("/cheese/login")
.permitAll();
http.csrf().disable();
}
}
UserController.java
package com.example.demo.controllers;
import com.example.demo.models.Customer;
import com.example.demo.models.data.CustomerDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
#Controller
#RequestMapping("cheese")
public class UserController {
#Autowired
private CustomerDao customerDao;
#RequestMapping(value = "login")
public String loginPage(Model model) {
model.addAttribute("title", "Login Page");
return "cheese/login";
}
#RequestMapping(value = "account")
public String accountInfo(Model model) {
model.addAttribute("title", "Account Page");
return "cheese/account";
}
#GetMapping("signup")
public String displaySignUpForm(Model model) {
model.addAttribute("title", "Sign Up");
model.addAttribute("customer", new Customer());
return "cheese/signup";
}
#PostMapping(value = "signup")
public String processSignUp(Model model, #ModelAttribute Customer customer, Errors errors) {
if (errors.hasErrors()) {
return "cheese/signup";
}
customerDao.save(customer);
return "cheese/success";
}
}
Application.Properties
spring.datasource.url=jdbc:mysql://localhost:8889/******?useSSL=false
spring.datasource.username=****
spring.datasource.password=******
spring.jpa.database=MYSQL
spring.jpa.hibernate.ddl-auto = update
spring.jpa.show-sql=false
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect
Pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>test2</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>test2</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>bootstrap</artifactId>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>webjars-locator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<addResources>true</addResources>
</configuration>
</plugin>
</plugins>
</build>
</project>
The Spring Security configuration should applied with the Configuration annotation.
Remove #RequestMapping("cheese") from SecurityConfig
The correct configuration:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter
Had the same issue, I created a separate config class where I defined a DataSource bean
#Bean
DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setUrl("database_url");
dataSource.setUsername("username");
dataSource.setPassword("password");
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
return dataSource;
}
and in the main config file (in your case Security config) left DataSource #Autowired.
#Autowired
DataSource dataSource;
#Autowired
protected void configAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication().dataSource(dataSource);
}
And it worked. In case it is useful, I used these Gradle dependencies -
implementation 'mysql:mysql-connector-java:8.0.18'
implementation group: 'org.springframework', name: 'spring-jdbc', version: '5.3.21'
Apart from the #Configuration annotation, add #EnableAutoConfiguration which would attempt and configure code.
#Configuration
#EnableAutoConfiguration
public class SecurityConfig extends WebSecurityConfigurerAdapter
Also, rebuild your sources afterwards.

Categories