Spring-boot listener’s #autowire is not work on recent versions - spring-boot

Spring-boot listener’s #autowire is not work on recent versions
When using 2.3.1.RELEASE of Spring-boot everything is ok.
#WebListener
public class Listener implements ServletContextListener {
#Autowired private Environment env;
#Override public void contextInitialized(ServletContextEvent sce) { try { if (env == null) // is false
When using 2.6.2 of Spring-boot the env will be null
change pom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.1.RELEASE</version><!-- ******** 2.3.1.RELEASE - 2.6.2 ********** -->
<relativePath/>
</parent>
sample code is in this link

as an issue I created in the spring-boot project in GitHub, this is due to the changes in Spring Boot 2.4 to allow #WebListeners to register servlets and filters. The change in 2.4 requires the Servlet container to be responsible for creating the listener so Spring-based dependency injection is no longer available.
Generally speaking, #ServletComponentScan is intended for registering third-party components where you can't modify the code. If you're in a position to use #Autowired and the like then you should use #Component. An exception to this is where you need to be able to register servlets and filters from your listener. #WebListener should be used in this case.
result of this issue updated the release notes for 2.4.

Related

Jackson databinding issue with Maven

I'm facing an issue where I have a maven project with Java POJOs.
import lombok.Builder;
import lombok.Getter;
/**
* User DTO.
*/
#Builder
#Getter
public class UserDto {
private String username;
}
I'm have added this maven dependency to another Spring boot based project.
I'm getting the following exception during the application tests.
org.springframework.core.codec.CodecException: Type definition error:
nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of com.models.UserDto (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
However, whenever I add the spring boot starter parent dependency to the maven project I see no errors and the application works as expected.
Note: Also, the project works by adding a default constructor to the pojo. But my question is why would it work with the Spring boot starter added?
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>

What Is the Correct Way To Use AbstractReactiveWebInitializer

I've got a Spring WebFlux application running successfully as a standalone spring boot application.
I am attempting to run the same application in a Tomcat container, and following the documentation, I've created a class that extends AbstractReactiveWebInitializer. The class requires that I implement a method getConfigClasses that would return classes normally annotated with #Configuration. If the working spring boot app started with a class called ApplicationInitializer, then the resulting implementations would look like this:
#SpringBootApplication(scanBasePackages = "my.pkg")
#EnableDiscoveryClient
#EnableCaching
public class ApplicationInitializer {
public static void main(String... args) {
SpringApplication.run(ApplicationInitializer.class, args);
}
}
and
public class ServletInitializer extends AbstractReactiveWebInitializer {
#Override
protected Class<?>[] getConfigClasses() {
return new Class[] {ApplicationInitializer.class};
}
}
When deployed, the only thing that starts is ApplicationInitializer, none of the autoconfigured Spring Boot classes (Cloud Config, DataSource, etc) ever kick off.
The documenation states this is the class I need to implement, I just expected the remainder of the spring environment to "just work".
How should I be using this class to deploy a Reactive WebFlux Spring Boot application to a Tomcat container ?
Edit:
After some additional research, I've narrowed it down to likely just Cloud Config. During bean post processing on startup, the ConfigurationPropertiesBindingPostProcessor should be enriched with additional property sources (from cloud config), but it appears to be the default Spring properties instead, with no additional sources.
The misisng properties is causing downstream beans to fail.
Spring Boot does not support WAR packaging for Spring WebFlux applications.
The documentation you're referring to is the Spring Framework doc; Spring Framework does support that use case, but without Spring Boot.
you can extend SpringBootServletInitializer, add add reactive servlet on onStartup method

Spring boot metrics with Jersey2

I have an application running spring-boot, jersey2 and spring metrics:
below is maven snippet:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jersey</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
Jersey used to work well until introducing actuator dependency.
Then following bean has been created to make Jersey working as filter:
#Bean
public FilterRegistrationBean jerseyFilterRegistration() {
FilterRegistrationBean bean = new FilterRegistrationBean();
bean.setName("jerseyFilter");
bean.setFilter(new ServletContainer(resourceConfig()));
bean.setOrder(Ordered.LOWEST_PRECEDENCE);
bean.addInitParameter("com.sun.jersey.config.property.WebPageContentRegex", managementContextRegex);
return bean;
}
Metrics are mapped to /admin path. With this configuration I cannot make metrics working. However by adding management.port (different than main app port) both Jersey resource and metrics are available.
What I'm missing here to make both metrics and Jersey resource start working on the same port?
"com.sun.jersey.config.property.WebPageContentRegex"
This is the wrong property. That's for Jersey 1.x. For 2.x, it should be
"jersey.config.servlet.filter.staticContentRegex"
See ServletProperties.FILTER_STATIC_CONTENT_REGEX
As an aside you can avoid having to define your own FilterRegistrationBean by simply setting a couple configuration properties. In your application.properties, you could use the following
spring.jersey.type=filter
spring.jersey.init.jersey.config.servlet.filter.staticContentRegex=<your-regex>
Or you can configure the regex in your ResourceConfig subclass
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
property(ServletProperties.FILTER_STATIC_CONTENT_REGEX, "<your-regex>");
}
}
As another side, just an FYI, the cause of the problem is the default /* url-mapping used for Jersey. If you can change it, doing so would solve the problem. For instance /api. You can configure that in the properties with spring.jersey.applicationPath=/api or with #ApplicationPath("/api") on the ResourceConfig subclass.
And the final aside, there is also a property
ServletProperties.FILTER_FORWARD_ON_404
"jersey.config.servlet.filter.forwardOn404"
I'm not exactly sure how the staticContenRegex property works, I never really dug into to source code. So I don't know if it just does some file IO to get the static file or it forwards the request, but if it does some file IO, then I don't think the property will work for your use case, as the endpoints are not files. In which case the forwardOn404 should work.

Spring-boot not respecting liquibase properties

I'm in the process of setting up liquibase to manage my database in a new spring boot application. I need the liquibase dependency in my classpath to reset the database state after certain integration tests run. During my tests I do not want liquibase to be enabled via spring auto config during application context initialization. I've tried adding liquibase.enabled = false to the application.properties, however when I debug the LiquibaseAutoConfiguration class it appears that enabled is always set to true.
I'm not new to spring, but I am new to spring-boot's auto configuration. Has anyone had issues with spring boot not respecting properties in application.properties?
My setup is fairly minimal:
Relevant code snippets:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { SpringBootClass.class })
public class databaseTests{
#Before
public void setup() throws LiquibaseException, SQLException {
Database database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(new JdbcConnection(dataSource.getConnection()));
Liquibase liquibase = new Liquibase("db/changelog/db.changelog-master.yaml", new FileSystemResourceAccessor("src/main/resources/"),database );
liquibase.dropAll();
liquibase.update("test");
}
..
}
#SpringBootApplication
#Import({ DataSourceConfig.class, HibernateConfig.class, OauthConfig.class })
#EnableConfigurationProperties
public class SpringBootClass {
..
}
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.6.RELEASE</version>
<!-- <liquibase.version>3.3.5</liquibase.version> -->
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
<scope>test</scope>
</dependency>
If you want your tests to consume application.properties you need to run them as a Spring Boot application. Your use of #ContextConfiguration means that you're currently running them as a vanilla Spring Framework application. Replace the #ContextConfiguration annotation with #SpringApplicationConfiguration.
Should have RTFM...
from spring boot documentation
ConfigFileApplicationContextInitializer is an
ApplicationContextInitializer that can apply to your tests to load
Spring Boot application.properties files. You can use this when you
don’t need the full features provided by
#SpringApplicationConfiguration.
#ContextConfiguration(classes = Config.class,
initializers = ConfigFileApplicationContextInitializer.class)
Changing my config to use the initializer worked.

drools-6 (kie) auto scanning (from spring) of modules and sessions from kie workbench deployed artifacts

I am trying to build a web (spring-mvc) application with kie (drools 6) integrated via injection. I have used kie workbench to create a workflow, complied and deployed. I have added reference of this artifact in my project's pom.xml and added the local kie-workbench repository as per this blog post and it's working fine (pulling in the artifact as dependency in my maven/spring project). What I am trying to do is inject the kiesession in one of my service as dependency with following snippet -
#Service
public class TniServiceImpl implements TniService {
#Inject
#KSession("tniSession")
private KieSession tniSession;
...
}
In my root-context.xml, I have added the kie namespace as well along with reference to xsd. I have added org.kie.spring.KModuleBeanFactoryPostProcessor as well as per drools documentation. I am trying to make CDI injection work for KSession scanning and injection (it's already working for my other components in same project, using #Inject). So far I am always getting "No qualifying bean of type [org.kie.api.runtime.KieSession] found for dependency" error. Looks like spring is not able to scan the available kie modules and sessions therein. Need help on following -
Is CDI inject really supported with spring? Do I have to configure kmodules and kession explicitly as mentioned here?
Am I missing something here which should make this scanning and injection work?
My environment is following -
spring 3.2.6-RELEASE (including webmvc and other components)
kie-api-6.0.1.FINAL
kie-spring-6.0.1.FINAL
kie-internal-6.0.1.FINAL
I have already gone through following links but no luck (mostly they are not trying to do what I am) -
Loading Drools/KIE Workbench artifacts directly from the repository
why does loading Drools 6 KIE JAR into code fail?
I'll appreciate if anybody can guide me on what could be the missing piece here or if there's no option but to explicitly define all kmodules/ksessions in spring config file.
I had the same problem and found a solution here: http://drools.46999.n3.nabble.com/Spring-4-0-amp-Drools-6-0-1-Integration-issue-td4028052.html
Basically you will need to inject ApplicationContext instead of kieSession and get xml bean manually.
TniServiceImpl.java
#Service
public class TniServiceImpl implements TniService {
#Inject
ApplicationContext context;
KieSession kieSession;
#PostConstruct
public void postConstruct(){
kieSession = (KieSession) context.getBean("ksession1");
}
...
}
root-context.xml
<kie:kmodule id="kmodule1">
<kie:kbase name="kbase1">
<kie:ksession name="ksession1" />
</kie:kbase>
</kie:kmodule>
<bean id="kiePostProcessor" class="org.kie.spring.KModuleBeanFactoryPostProcessor" />
Hope this helps.
UPDATE:
Another way to achieve this is to keep xml identical and instead of trying to inject KieSession, inject KieBase. Then, with the instance of KieBase, create new KieSessions.
#Service
public class TniServiceImpl implements TniService {
#Autowired
private KieBase kbase;
/* inside some method */
#RequestMapping(method=RequestMethod.GET)
public #ResponseBody Data getData() {
KieSession ksession = kbase.newKieSession();
...
}
}
The above answer doesn't work with spring mvc. I found that this is a bug in the existing drools and they are fixing it in the next version. I am stuck at this point since I am using DROOLS in batch mode but I want it to be used in a REST Service hosted on websphere.
The above solution works perfectly within a batch program.
This is what I have working with the latest Spring MVC (Spring Boot)
#SpringBootApplication
public class DroolDemoApplication {
public static void main(String[] args) {
SpringApplication.run(DroolDemoApplication.class, args);
}
#Bean
public KieContainer kieContainer() {
return KieServices.Factory.get().getKieClasspathContainer();
}
#Bean
public KieSession kieSession() throws IOException {
return kieContainer().newKieSession("DroolDemoSession");
}
}
and below is the kmodule.xml
<kbase name="DroolDemoKbase" packages="rules">
<ksession name="DroolDemoSession" />
</kbase>
finally all you do in your controller is
#Autowired
private KieSession kieSession;
kieSession.fireAllRules();
hope this helps those folks still having issues
I had similar issues with the rules not being triggered, and I solved it by using the 6.2.0.Final version of the kie-ci and kie-spring. I tried versions: 7.7.0, 7.2.0, 6.5.0 and 6.4.0, but none of them worked.
...
<properties>
<kie.version>6.2.0.Final</kie.version>
</properties>
...
<dependencies>
...
<dependency>
<groupId>org.kie</groupId>
<artifactId>kie-ci</artifactId>
<version>${kie.version}</version>
</dependency>
<dependency>
<groupId>org.kie</groupId>
<artifactId>kie-spring</artifactId>
<version>${kie.version}</version>
</dependency>
...
</dependencies>
What also helped was running mvn dependency:tree and seeing which versions of which artefacts/projects are being used.

Resources