Native Image - Spring Boot - AWS Serverless Java Container - startup error - missing ServletWebServerFactory bean - spring-boot

I am trying to create a native image for my Spring Boot application that also uses the AWS serverless Java container.
The Spring Boot app is a simple petstore application.
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<!-- Comment for local -->
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
<!-- Not using yaml -->
<exclusion>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-indexer</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.amazonaws.serverless</groupId>
<artifactId>aws-serverless-java-container-springboot2</artifactId>
<version>1.8</version>
</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> -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
<!-- <filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>**/Log4j2Plugins.dat</exclude>
</excludes>
</filter>
</filters> -->
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<artifactSet>
<excludes>
<exclude>org.apache.tomcat.embed:*.jar</exclude>
</excludes>
</artifactSet>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
Entry point
public class StreamLambdaHandler implements RequestStreamHandler {
private static SpringBootLambdaContainerHandler<HttpApiV2ProxyRequest, AwsProxyResponse> handler;
private static Logger logger = LoggerFactory.getLogger(StreamLambdaHandler.class);
public static void main(String[] args) {
}
static {
try {
logger.info("StreamLambdaHandler static init start");
//handler = SpringBootLambdaContainerHandler.getHttpApiV2ProxyHandler(SamplebootApplication.class);
handler = new SpringBootProxyHandlerBuilder<HttpApiV2ProxyRequest>()
.defaultHttpApiV2Proxy() .asyncInit()
.springBootApplication(SamplebootApplication.class) .buildAndInitialize();
logger.info("StreamLambdaHandler init -async init call ");
logger.info("StreamLambdaHandler init call invoked");
} catch (ContainerInitializationException e) {
// if we fail here. We re-throw the exception to force another cold start
e.printStackTrace();
throw new RuntimeException("Could not initialize Spring Boot application", e);
}
}
#Override
public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) throws IOException {
Instant start = Instant.now();
logger.info("StreamLambdaHandler handler method begin");
handler.proxyStream(inputStream, outputStream, context);
logger.info(
"StreamLambdaHandler handler method end in millisecs:" + start.until(Instant.now(), ChronoUnit.MILLIS));
}
The application works when deployed on AWS lambda. I am now trying to optimise the cold start time by converting to Native Image.
Added spring native
<dependency>
<!-- This is a mandatory dependency for your application -->
<groupId>org.springframework.experimental</groupId>
<artifactId>spring-native</artifactId>
<version>0.11.4</version>
</dependency>
Removed shade plugin, added AOT plugin
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.springframework.experimental</groupId>
<artifactId>spring-aot-maven-plugin</artifactId>
<version>0.11.4</version>
<executions>
<execution>
<id>generate</id>
<goals>
<goal>generate</goal>
</goals>
</execution>
<!-- <execution> <id>test-generate</id> <goals> <goal>test-generate</goal>
</goals> </execution> -->
</executions>
</plugin>
The executable jar - works fine in local.
Edit:
The jar fails with the same error as below when run with springAot=true.
The jar is now converted to Native Image using below steps.
rm -rf native
mkdir -p native
cd native
jar -xvf ../samplebootv2-native-0.0.1-SNAPSHOT.jar >/dev/null 2>&1
cp -R META-INF BOOT-INF/classes
native-image -H:Name=samplebootv2-native-app -cp BOOT-INF/classes:`find BOOT-INF/lib | tr '\n' ':'`
Image is build without errors. However, when the native image is excecuted it fails with following error.
org.springframework.context.ApplicationContextException: Unable to start web server; nested exception is org.springframework.context.ApplicationContextException: Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean.
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:163) ~[samplebootv2-native-app:2.6.6]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:577) ~[samplebootv2-native-app:5.3.18]
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:145) ~[samplebootv2-native-app:2.6.6]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:740) ~[samplebootv2-native-app:2.6.6]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:415) ~[samplebootv2-native-app:2.6.6]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:303) ~[samplebootv2-native-app:2.6.6]
at org.springframework.boot.builder.SpringApplicationBuilder.run(SpringApplicationBuilder.java:164) ~[na:na]
at com.amazonaws.serverless.proxy.spring.SpringBootLambdaContainerHandler.initialize(SpringBootLambdaContainerHandler.java:195) ~[samplebootv2-native-app:na]
at com.amazonaws.serverless.proxy.AsyncInitializationWrapper$AsyncInitializer.run(AsyncInitializationWrapper.java:120) ~[na:na]
at java.lang.Thread.run(Thread.java:829) ~[samplebootv2-native-app:na]
at com.oracle.svm.core.thread.PlatformThreads.threadStartRoutine(PlatformThreads.java:704) ~[samplebootv2-native-app:na]
at com.oracle.svm.core.posix.thread.PosixPlatformThreads.pthreadStartRoutine(PosixPlatformThreads.java:202) ~[na:na]
Caused by: org.springframework.context.ApplicationContextException: Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean.
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.getWebServerFactory(ServletWebServerApplicationContext.java:210) ~[samplebootv2-native-app:2.6.6]
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.createWebServer(ServletWebServerApplicationContext.java:180) ~[samplebootv2-native-app:2.6.6]
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.onRefresh(ServletWebServerApplicationContext.java:160) ~[samplebootv2-native-app:2.6.6]
... 11 common frames omitted
I checked the generated image using graal vm visualiser and found that the classes from the aws-serverless-java-container-springboot2-1.8.jar were missing in the image
So I added the class files (only few of them) in
META-INF\native-image\org.springframework.aot\spring-aot\reflect-config.json
Also tried(with both init modes)
#InitializationHint(types =com.amazonaws.serverless.proxy.spring.embedded.ServerlessServletEmbeddedServerFactory.class, initTime = InitializationTime.RUN)
However, the same error repeats. The classes are missing in the generated image.
Could anyone provide some insights into what could be going wrong here?

Related

Asciidoctor "Snippets" is missing in Maven

When I run the test case then requests and responses of adoc type will be created and displayed in JSON format under the generated-snippets directory. when I run this mvn command mvn clean package to create jar and HTML type of index under the generated-docs then following warning has occurred
when I open the index.html via browser then Unresolved directive in index.adoc is displayed instead of JSON result as a response.
pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-jpa -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/com.oracle/ojdbc6 -->
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
<version>11.2.0.4</version>
</dependency>
<dependency>
<groupId>org.springframework.restdocs</groupId>
<artifactId>spring-restdocs-asciidoctor</artifactId>
<version>2.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.restdocs</groupId>
<artifactId>spring-restdocs-mockmvc</artifactId>
<version>2.0.5.RELEASE</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<finalName>config-demo</finalName>
</configuration>
</plugin>
<plugin>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctor-maven-plugin</artifactId>
<version>1.5.6</version>
<executions>
<execution>
<id>generate-docs</id>
<phase>package</phase>
<goals>
<goal>process-asciidoc</goal>
</goals>
<configuration>
<backend>html</backend>
<doctype>product</doctype>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
index.adoc
= Nafaz Benzema Getting Started With Spring REST Docs
This is an example output for a service running at http://localhost:9090:
==GET API Example
.request
include::{snippets}/getAllProduct/http-request.adoc[]
.response
include::{snippets}/getAllProduct/http-response.adoc[]
As you can see the format is very simple, and in fact you always get the same message.
Controller Test class
#ExtendWith({RestDocumentationExtension.class, SpringExtension.class})
#WebMvcTest
#AutoConfigureRestDocs(outputDir = "/target/generated-snippets")
public class ProductControllerTest {
#Autowired
private WebApplicationContext webApplicationContext;
#MockBean
private ProductService productService;
private MockMvc mockMvc;
List<Product> products =null;
#BeforeEach
public void setUp(WebApplicationContext webApplicationContext, RestDocumentationContextProvider documentationContextProvider) {
this.mockMvc = MockMvcBuilders
.webAppContextSetup(webApplicationContext)
.apply(MockMvcRestDocumentation.documentationConfiguration(documentationContextProvider))
.build();
products=getProducts();
}
#Test
public void getAllProduct() throws Exception {
String expectedProduct=new ObjectMapper().writeValueAsString(products);
Mockito.when(productService.getAllProduct()).thenReturn(products);
MvcResult result= mockMvc.perform(get("/products")
.contentType(MediaType.APPLICATION_JSON_VALUE))
.andExpect(status().isOk())
.andExpect(content().json(expectedProduct))
.andDo(document("{methodName}",preprocessRequest(prettyPrint()),
preprocessResponse(prettyPrint())))
.andReturn();
String actualProduct=result.getResponse().getContentAsString();
Assertions.assertEquals(expectedProduct,actualProduct);
}
private List<Product> getProducts()
{
Product product_1=new Product();
product_1.setProductId(1001);
product_1.setProductName("Penguin-ears");
product_1.setNumberOfUnitInCartoon(20);
product_1.setPriceOfCartoon(175.00);
product_1.setUrlOfImage("https://i.ibb.co/pLdM7FL/shutterstock-306427430-scaled.jpg");
Product product_2=new Product();
product_2.setProductId(1002);
product_2.setProductName("Horseshoe");
product_2.setNumberOfUnitInCartoon(5);
product_2.setPriceOfCartoon(825);
product_2.setUrlOfImage("https://i.ibb.co/MRDwnqj/horseshoe.jpg");
return new ArrayList<>(Arrays.asList(product_1,product_2));
}
}
The solution has been found. We have to add spring-restdocs-asciidoctor dependency inside the plugin which it belongs.
Remove spring-restdocs-asciidoctor dependency from the global dependency management space and add it inside the plugin
<plugin>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctor-maven-plugin</artifactId>
<version>1.5.6</version>
<executions>
<execution>
<id>generate-docs</id>
<phase>package</phase>
<goals>
<goal>process-asciidoc</goal>
</goals>
<configuration>
<backend>html</backend>
<doctype>product</doctype>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.springframework.restdocs</groupId>
<artifactId>spring-restdocs-asciidoctor</artifactId>
<version>2.0.5.RELEASE</version>
</dependency>
</dependencies>
</plugin>

Adding a JNI library to a spring boot (maven) jar

I'm using Google Or-Tools library over a Java-Spring-Boot app, Windows 10 OS and Intellij IDE.
To make it work over intellij I did the following:
Install Microsoft Visual C++ Redistributable for Visual Studio 2019 (required according to the installation instructions).
Downloaded and extracted the OR-Tools library for Java (included 2 jars and a 1 dll file).
In Intellij, I added those jars as module dependencies (under a folder called lib).
Added the lib library path to VM options in Intellij run configurations.
Loaded the library statically in my code:
static {System.loadLibrary("jniortools");}
Now I can run the project successfully form Intellij.
Next I would like to pack everything to a spring boot jar that can run over any windows machine.
My folders structure is:
My pom file is pretty basic, a few dependencies with a standard spring-boot-maven-plugin:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
As I'm trying to pack the code using mvn install I'm getting package com.google.ortools.sat does not exist.
How can I make sure maven packs those 3rd party jars to the executable spring-boot jar?
UPDATE
I added to my pom file:
<dependency>
<groupId>com.google.ortools</groupId>
<artifactId>ortools</artifactId>
<version>LATEST</version>
<type>jar</type>
<scope>system</scope>
<systemPath>${project.basedir}/lib/com.google.ortools.jar</systemPath>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>LATEST</version>
<type>jar</type>
<scope>system</scope>
<systemPath>${project.basedir}/lib/protobuf.jar</systemPath>
</dependency>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.7</version>
<executions>
<execution>
<phase>generate-test-sources</phase>
<configuration>
<tasks>
<mkdir dir="${project.basedir}/target/lib"/>
<echo message="Creating lib folder..."/>
<copy todir="${project.basedir}/target/lib">
<fileset dir="${project.basedir}/lib">
<include name="**/**"/>
</fileset>
</copy>
</tasks>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
In addition adding to library path:
static {
try {
String orToolsDllLibrary = System.getProperty("user.dir") + "\\lib";
addLibraryPath(orToolsDllLibrary);
System.loadLibrary("jniortools");
} catch (Exception e) {
e.printStackTrace();
}
}
public static void addLibraryPath(String pathToAdd) throws Exception {
final Field usrPathsField = ClassLoader.class.getDeclaredField("usr_paths");
usrPathsField.setAccessible(true);
//get array of paths
final String[] paths = (String[]) usrPathsField.get(null);
//check if the path to add is already present
for (String path : paths) {
if (path.equals(pathToAdd)) {
return;
}
}
//add the new path
final String[] newPaths = Arrays.copyOf(paths, paths.length + 1);
newPaths[newPaths.length - 1] = pathToAdd;
usrPathsField.set(null, newPaths);
}
And now when running command java -jar myApp-0.0.1-SNAPSHOT.jar getting an exception:
Caused by: java.lang.NoClassDefFoundError: com/google/ortools/sat/CpSolver
at java.base/java.lang.Class.getDeclaredMethods0(Native Method) ~[na:na]
at java.base/java.lang.Class.privateGetDeclaredMethods(Class.java:3167) ~[na:na]
at java.base/java.lang.Class.getDeclaredMethods(Class.java:2310) ~[na:na]
at org.springframework.util.ReflectionUtils.getDeclaredMethods(ReflectionUtils.java:463) ~[spring-core-5.2.6.RELEASE.jar!/:5.2.6.RELEASE]
... 29 common frames omitted
Caused by: java.lang.ClassNotFoundException: com.google.ortools.sat.CpSolver
at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:471) ~[na:na]
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:588) ~[na:na]
at org.springframework.boot.loader.LaunchedURLClassLoader.loadClass(LaunchedURLClassLoader.java:129) ~[solver-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521) ~[na:na]
... 33 common frames omitted
I am not sure how you added the library to your project? You don't seem to have done it through Maven, did you?
In the past I took the approach of adding it via using system scope in Maven (see here. This would give you something like this in your pom:
<dependency>
<groupId>com.google.ortools</groupId>
<artifactId>ortools</artifactId>
<version>1.0</version>
<scope>system</scope>
<systemPath>${basedir}/src/main/resources/lib/com.google.ortools.jar</systemPath>
</dependency>
However, this approach can also be a pain especially if you have to work multi-platform. Recently, I found this repo and that made my life much easier dealing with OR-tools. Hope this helps.
UPDATE:
I strongly recommend using the updated method below as it is much less of a headache:
<repositories>
...
<repository>
<id>bintray</id>
<url>https://dl.bintray.com/magneticflux/maven</url>
</repository>
....
</repositories>
<dependencies>
<dependency>
<groupId>com.skaggsm.ortools</groupId>
<artifactId>ortools-natives-all</artifactId>
<version>7.7.7810</version>
</dependency>
<!-- OR-tools needs protobuf -->
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>3.12.2</version>
</dependency>
</dependencies>
Then you can do a static load of the library:
static {
OrToolsHelper.loadLibrary()
}
Make sure to work with JDK >= 11 as elaborated here.
I tried:
First add the jniortools library to java.library.path pragmatically:
static {
String orToolsDllLibrary = System.getProperty("user.dir") + "\\lib";
addLibraryPath(orToolsDllLibrary);
System.loadLibrary("jniortools");
}
public static void addLibraryPath(String pathToAdd) throws Exception {
final Field usrPathsField = ClassLoader.class.getDeclaredField("usr_paths");
usrPathsField.setAccessible(true);
//get array of paths
final String[] paths = (String[]) usrPathsField.get(null);
//check if the path to add is already present
for (String path : paths) {
if (path.equals(pathToAdd)) {
return;
}
}
//add the new path
final String[] newPaths = Arrays.copyOf(paths, paths.length + 1);
newPaths[newPaths.length - 1] = pathToAdd;
usrPathsField.set(null, newPaths);
}
In pom file:
<dependencies>
....
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>3.12.2</version>
</dependency>
<dependency>
<groupId>com.google</groupId>
<artifactId>ortools</artifactId>
<version>0.0.2</version>
</dependency>
....
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
<executions>
<execution>
<id>install-external</id>
<phase>process-resources</phase>
<configuration>
<file>${basedir}/lib/com.google.ortools.jar</file>
<repositoryLayout>default</repositoryLayout>
<groupId>com.google</groupId>
<artifactId>ortools</artifactId>
<version>0.0.2</version>
<packaging>jar</packaging>
<generatePom>true</generatePom>
</configuration>
<goals>
<goal>install-file</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.7</version>
<executions>
<execution>
<phase>generate-sources</phase>
<configuration>
<tasks>
<mkdir dir="${project.basedir}/target/lib"/>
<echo message="Creating lib folder..."/>
<copy todir="${project.basedir}/target/lib">
<fileset dir="${project.basedir}/lib">
<include name="**/**"/>
</fileset>
</copy>
</tasks>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

How to instantiate groovy.sql.Sql with parameters from spring datasource setup from test application.yml in a SpringBoot application test context?

I have a SpringBoot maven project, and it has a repository related jar sub-module, which stores the DB related executions, the repository classes with JdbcTemplate and etc.
I want to test the database with Spock Groovy.
This is my pom.xml:
...
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
</dependency>
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
</dependency>
<!-- Spock Dependencies -->
<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-spring</artifactId>
<scope>test</scope>
</dependency>
<!-- enables mocking of classes (in addition to interfaces) -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<scope>test</scope>
</dependency>
<!-- enables mocking of classes without default constructor (together with CGLIB) -->
<dependency>
<groupId>org.objenesis</groupId>
<artifactId>objenesis</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.codehaus.groovy.modules.http-builder</groupId>
<artifactId>http-builder</artifactId>
<scope>test</scope>
</dependency>
<!-- Spock End -->
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<includes>
<include>**/*Spec.*</include>
<include>**/*Test.*</include>
</includes>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.gmavenplus</groupId>
<artifactId>gmavenplus-plugin</artifactId>
<version>1.5</version>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>test-jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
...
This is the application.yml from test/resources:
spring:
datasource:
platform: sqlserver
driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver
url: jdbc:sqlserver://localhost;databaseName=mydatabase;integratedSecurity=true
server:
address: 127.0.0.1
port: 9000
This is the application config in main/java/mypackage:
#Configuration
#ComponentScan
#EnableAutoConfiguration
public class RepositoryConfig {
}
This is the groovy test spec which should setup the groovy.sql.Sql:
#SpringBootTest(classes = RepositoryConfig.class, webEnvironment = WebEnvironment.MOCK)
#Configuration
#EnableConfigurationProperties
#Transactional
class BaseSpringBootTestSpec extends Specification {
String url
String username
String password
String driverClassName
protected groovy.sql.Sql sql
def setup() {
sql = groovy.sql.Sql.newInstance(url, username, password, driverClassName)
}
def cleanup() {
sql.close()
}
}
But here I get NullPointerException because the properties are all null (url, driverClassName, etc).
And I don't know how to get them from yml properties in text context.
Any idea? Thanks.
Blockquote
You're never telling Spring to actually inject those values. Adding #Value annotations to your fields should fix your problem. E.g.:
#Value("#{spring.datasource.url}")
String url
Probably easiest to use theSql.newInstance(...) constructor, passing along a DataSource automatically injected from your Spring Boot config.
#Autowired
DataSource apexDatasoure
...And later when you want to perform some Sql:
Sql.newInstance(apexDatasoure).with { db ->
...
}

AspectJ + Junit + Maven + Java8

I followed this SO question and tried to implement it for java8. My project is not a spring project.
Aspect
#Aspect
public class MethodLogger {
#Pointcut("execution(#org.junit.Test * *())")
public void testMethodEntryPoint() {}
#Before("testMethodEntryPoint()")
public void executeBeforeEnteringTestMethod() {
System.out.println("EXECUTE ACTION BEFORE ENTERING TEST METHOD");
}
#After("testMethodEntryPoint()")
public void executeAfterEnteringTestMethod() {
System.out.println("EXECUTE ACTION AFTER ENTERING TEST METHOD");
}
}
JUnit Test
#RunWith(JUnit4.class)
public class POSTaggerTest {
#Test
public void test() {
...
}
}
POM.xml
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.7</version>
<configuration>
<aspectLibraries>
<aspectLibrary>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</aspectLibrary>
</aspectLibraries>
<!-- java version -->
<complianceLevel>1.8</complianceLevel>
<source>1.8</source>
<target>1.8</target>
<!-- End : java version -->
<verbose>true</verbose>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>test-compile</goal>
</goals>
<configuration>
<showWeaveInfo>true</showWeaveInfo>
</configuration>
</execution>
</executions>
</plugin>
:
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>1.8.6</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.2</version>
</dependency>
<dependency>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.7</version>
<type>maven-plugin</type>
</dependency>
I don't see any error. Am I missing something? Or any wrong artifact? What I want when I run junit tests, all the aspects whould work fine.
The situation you want to recreate is like this:
There is one Maven module with aspects. It is compiled with AspectJ Maven Plugin.
There is another module with the actual application and test code. It is also compiled with AspectJ Maven, this time referring to the first module as an aspect library.
What you are doing though is:
You have a single module. This is not a problem in and of itself because it is easily possible to keep aspects and Java code within the same module if you want the aspects applied on this module only.
But now you declare JUnit as an aspect library. Why? It does not contain any aspects. You should remove that declaration.

How to inject a bean in Hibernate UserType object?

I need to save some entity fields in DB as JSON.
Favorite solution is defining a custom hibernate UserType.
JSON converter (Jackson) doc recommends using it as a singleton but hibernate itself creates custom UserType object.
How can I inject spring defined JSON convertor bean in my custom Hibernate UserType object?
The solution could be using #Configurable so you will able to autowire attribute objects even if that is not the spring container that intenciate them.
see the spring documentation : http://static.springsource.org/spring/docs/3.2.3.RELEASE/spring-framework-reference/html/aop.html#aop-atconfigurable
code exemple :
#Configurable
public class CSessionImpl implements CSessionOperations {
private Touriste touriste;
#Inject
private Office office;
#Inject
private OTmanager manager;
#Inject private ScheduledExecutorService executorService;
private ScheduledFuture<Void> schedule;
#PostConstruct
private void replanifierMiseHorsLigne(){
if(schedule != null){
schedule.cancel(false);
}
schedule = executorService.schedule(new Callable<Void>() {
#Override
public Void call() t
see in this exemble when CSessionImpl.< init> will be call the attribut with #Inject will be wired.
you nedd to add in beans.xml :
<context:spring-configured/>
You also need to perform weaving at compile-time or runtime
Exemple for maven for compile-time weaving:
<build>
<finalName>OTLogiciel</finalName>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.4</version>
<executions>
<execution>
<id>compile</id>
<configuration>
<showWeaveInfo>true</showWeaveInfo>
<source>${java-version}</source>
<target>${java-version}</target>
<verbose>false</verbose>
<outxml>true</outxml>
<aspectLibraries>
<aspectLibrary>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</aspectLibrary>
</aspectLibraries>
</configuration>
<goals>
<goal>compile</goal>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>persistence-api</artifactId>
<version>1.0</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${org.aspectj-version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>${org.aspectj-version}</version>
</dependency>
</dependencies>
</plugin>
If you're using eclispe you're also need to install "ApectJ developement tools"

Resources