Where to put Groovy bean definitions in a Spring Boot webapp? - spring

Using Spring Boot I can get a jar created with my webapp (allowing me to execute it as java -jar test-0.1.0.jar as I want). I would like to configure the Spring beans using the groovy bean def syntax, with a separate file like beans.groovy that would contain the definitions.
How can I have that groovy file included in my jar and how can I tell Spring to load it?
I've seen a few examples around the web showing how to define beans this way together with spring boot, but none explain how to get that groovy file included in the jar and run as a webapp.

SpringApplication accepts Resource paths as sources, so just include a String ending in ".groovy", e.g.
public static main(String args[]) {
SpringApplication.run("classpath:beans.groovy", args);
}

Related

Creating a custom FactoryBean in Sprint Boot 2.3/Spring 5

I've got a spring-boot web application that's mostly working; my DataSource is properly configured by an external application.properties file.
Now I want to add properties to that file to help me instantiate and configure two instances of a class in my app. I have a APNsFactory that I currently instantiate manually and configure using JNDI, but I want to get away from JNDI calls:
#Bean
public
APNsFactory
apnsFactory()
throws
javax.naming.NamingException
{
sLogger.info("Configuring APNsFactory");
InitialContext ctx = new InitialContext();
APNsFactory f = new APNsFactory();
f.setProductionKeystorePath((String) ctx.lookup("java:comp/env/apns/prod/keystorePath"));
f.setProductionKeystorePassword((String) ctx.lookup("java:comp/env/apns/prod/keystorePassword"));
f.setDevelopmentKeystorePath((String) ctx.lookup("java:comp/env/apns/dev/keystorePath"));
f.setDevelopmentKeystorePassword((String) ctx.lookup("java:comp/env/apns/dev/keystorePassword"));
return f;
}
When running before in a standalone webapp container, Spring properly called that method and the JNDI context from the container’s <env-entry> tags was available.
I'm trying to update my APNsFactory to be a proper Spring FactoryBean<>, and I’ve given it a couple of #Autowire String variables that I want to be set by Spring Boot from the application.properties file.
For bonus points, I want this to be usable both in Spring Boot and in a standalone container like Tomcat or Resin.
For the life of me, I can't figure out how to get Spring to do this. There are dozens of examples for DataSources and other Beans already implemented by Spring, but none for a completely custom one, using application.properties, in a Spring Boot web environment.
I've seen some examples that use an XML config file, but I'm not sure how to do that with Spring Boot.
I don't think you need a factory bean here.
You already have spring boot that can read application.properties out-of-the-box:
So try the following:
Create key/values in the application.properties file:
myapp.keystore.path=...
myapp.keystore.passwd=...
// the same for other properties
Create ConfigurationProperties class
#ConfigurationProperties(prefix="myapp.keystore")
public class MyAppKeyStoreConfigProperties {
private String path; // the names must match to those defined in the properties file
private String passwd;
... getters, setters
}
In the class marked with #Configuration (the one where you create #Bean public APNsFactory apnsFactory()) do the following:
#Configuration
// Note the following annotation:
#EnableConfigurationProperties(MyAppKeyStoreConfigProperties.class)
public class MyConfiguration {
// Note the injected configuration parameter
#Bean public APNsFactory apnsFactory(MyAppKeyStoreConfigProperties config) {
APNsFactory f = new APNsFactory();
f.setProductionKeystorePath(config.getKeyPath());
and so on
}
}
I've intentionally didn't show the separation between production/dev stuff.
In spring boot you have profiles so that the same artifact (WAR, JAR whatever) can be configured to run with different profile and depending on that the corresponding properties will be read.
Example:
If you're running with prod profile, then in addition to application.properties that will be loaded anyway, you can put these keystore related definitions to application-prod.properties (the suffix matches the profile name) - spring boot will load those automatically. The same goes for dev profile of course.
Now I haven't totally understand the "bonus points" task :) This mechanism is spring boot proprietary way of dealing with configuration. In "standalone" server it should still have a WAR with spring boot inside so it will use this mechanism anyway. Maybe you can clarify more, so that I / our colleagues could provide a better answer

Spring Boot Override Properties Externally

I have a Spring Boot application that is getting deployed as a WAR to a Tomcat server. There is already a currently deployed Spring Boot application on Tomcat server that has an application.properties file on classpath that is overriding the one bundled in the WAR. I need to do the same thing for my application, but I can't use the name application.properties as it is already taken, and my application is trying to use the application.properties on the classpath from Tomcat, which is intended for the other Spring Boot application.
Is there a way I can tell Spring Boot to look for a properties file on the classpath called myapp.properties or something along those lines?
I tried doing the following, but it doesn't seem to work when being deployed as a WAR.
#SpringBootApplication
public class ParameterManagerApplication {
public static void main(String[] args) {
new SpringApplicationBuilder(ParameterManagerApplication.class)
.properties("spring.config.name:parameter-portal")
.build()
.run(args);
}
}
By default, Spring Boot looks for the application.properties file in these locations.
/config subdir of the working directory
The working directory
config package in the classpath
classpath root
So, the following worked for us when we placed myserver.properties under any of these locations.
public static void main(String[] args){
System.setProperty("spring.config.name","myserver");
SpringApplication.run(Application.class,args);
}
Otherwise, You can try setting the spring.config.location as below.
new SpringApplicationBuilder(Application.class)
.properties("spring.config.name:application,myserver",
"spring.config.location:classpath:/external/myproperties/")
.build().run(args);
We can also optionally define a custom source where we’re storing these properties, else the default location (classpath:application.properties) is looked up. So we now add the above annotations to the existing properties class:
#Configuration
#PropertySource("classpath:configprops.properties")
#ConfigurationProperties(prefix = "dev")
public class ConfigProperties {
// previous code
}
Now any properties defined in the property file that has the prefix dev and the same name as one of the properties are automatically assigned to this object.
#Simple properties
dev.host=mailer#mail.com
dev.port=9000
Check this

How to integration test auto configuration for a custom Spring Boot style starter library?

I am writing a library to provide some functionality that is shared between multiple different Spring Boot applications that I work with.
I would like to do something similar to the auto-configuration that is provided by the many Spring Boot starter libraries exist. That, or some other simple declarative way to integrate my library with the ApplicationContext of the apps using it.
I have found some resources explaining how auto configuration works. I can figure out the above problem.
However, I have not been able to find any good examples of how I can test as part of my library's test suite that it suitably integrates with a Spring Boot application. Ideally, I would start up a simple Spring Boot app written in the library's test directly just for the sake of testing, add the right annotation to it, and be able to assert that the correct beans are then configured.
I have tried creating a TestApplication class that does that and writing integration tests using the SpringBootTest annotation but the TestApplication was never started before my test started.
What can I do to start up a simple app like that solely for the purpose of testing my library? My tests are written with Spock and Spock-Spring in case that changes things versus other test frameworks.
I was able to make it work with the following test class:
#SpringBootTest
#ContextConfiguration(classes = TestApplication)
class DummyIntegrationSpec extends Specification {
#Autowired
DummyService dummyService
void 'dummy service should exist'() {
expect:
dummyService.getMessage() == DummyConfiguration.MESSAGE
}
}
and this TestApplication class at src/test/groovy/com/example/project/TestApplication.groovy
#SpringBootApplication(scanBasePackages = 'com.example.project.config')
#EnableAutoConfiguration
class TestApplication extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(TestApplication)
}
static void main(String[] args) {
SpringApplication.run(TestApplication, args)
}
}
The two key changes I had to make in order for the TestApplication to start and load the correct context when I moved my TestApplication class from src/main to src/test were:
the TestApplication class needed to be added to the ContextConfiguration annotation
the package that my library's Java config files live in needed to be added to the SpringBootApplication scanBasePackages field
The library auto-configuration does follow a similar structure to the one mentioned in the link tom provided.
Your auto-configuration should be automatically picked while your main spring application/test is starting and all beans will be registered in your context. They will be available for auto-wiring and follow your conditions and init order.
As a summary, make sure you have an auto-configuration annotated by #Configuration class with an #Import that imports your #Configuration annotated configuration classes (inside of them you define beans with methods annotated with #Bean). Also make sure you created a spring.factories file that include your auto-configuration class and that you removed the spring boot maven plugin (for the packaging to be right).
Also, make sure your auto-configuration project is NOT annotated by things like #SpringBootApplication, #EnableAutoConfiguration, #ComponentScan or other spring boot annotations that need to be only in the main spring boot projects (There should be one of them in each stack).
Please also see the article below:
Spring boot is based on a lot of pre-made auto-configuration parent projects. You should already be familiar with spring boot starter projects.
You can easily create your own starter project by doing the following easy steps:
Create some #Configuration classes to define default beans. You should use external properties as much as possible to allow customization and try to use auto-configuration helper annotations like #AutoConfigureBefore, #AutoConfigureAfter, #ConditionalOnBean, #ConditionalOnMissingBean etc. You can find more detailed information on each annotation in the official documentation Condition annotations
Place an auto-configuration file/files that aggregates all of the #Configuration classes.
Create a file named spring.factories and place it in src/main/resources/META-INF.
In spring.factories, set org.springframework.boot.autoconfigure.EnableAutoConfiguration property with comma separated values of your #Configuration classes:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=
com.mycorp.libx.autoconfigure.LibXAutoConfiguration,
com.mycorp.libx.autoconfigure.LibXWebAutoConfiguration
Using this method you can create your own auto-configuration classes that will be picked by spring-boot. Spring-boot automatically scan all maven/gradle dependencies for a spring.factories file, if it finds one, it adds all #Configuration classes specified in it to its auto-configuration process.
Make sure your auto-configuration starter project does not contain spring boot maven plugin because it will package the project as an executable JAR and won't be loaded by the classpath as intended - spring boot will not be able to find your spring.factories and won't load your configuration

Plugin System in Spring Boot for modular applications

I looking for dynamically loading jar in spring boot after compiling, for example I will put jars in some folder and when spring boot is started, all jars from this folder will be injected into spring boot app. I don't know how can I do this with spring boot, and if You know can help me with this, with some example.
I need this jars to have #Service, #Controller as this will be module (plugin), with adding capabilities to my spring boot app.
Is possible to do this with spring boot, and if it is possible, please provide me with some sample code.
Thanks in advance.
UPDATE:
I found something https://www.youtube.com/watch?v=F-sw2pFdcDw https://code.google.com/p/jspf/
UPDATE 2: I can't get #Controller bean from plugin jar registered in Spring Boot
Have a look at FlexiCore, an open-source framework that brings modularity to spring boot utilizing plugins(jars) loaded at runtime See wizzdi and FlexiCore.
for example FlexiCore allows you to create a project ( compiled into a seperate jar from your main application) that contains a spring bean as follows:
#Component
#Extension
public class HelloWorldService implements ServicePlugin{
public String hello() {
return "Hello World!";
}
}
it will be automatically be loaded once placed inside the designated plugins folder, it basically allows a full support for most(all) of spring boot features , so for example you can add a RestController bean to your jar as well , FlexiCore will automatically load that bean allowing you to call the controller as if it was in your main application jar:
#RestController
#Extension
public class TestEntityController implements Plugin {
private static final String template = "Hello, %s!";
private final AtomicLong counter = new AtomicLong();
#Autowired
private TestEntityService testEntityService;
#PostMapping("/createTestEntity")
public TestEntity createTestEntity(#RequestParam(name="name", required=false, defaultValue="Stranger") String name) {
return testEntityService.createTestEntity(name);
}
#GetMapping("{id}")
public TestEntity getTestEntity(#PathVariable("id")String id) {
return testEntityService.getTestEntity(id);
}
}
Disclaimer: I am the CTO of wizzdi, the company powering FlexiCore.
One option is definitely to just use broad #ComponentScan. If you add new jar to classpath the annotated classes from that jar will get discovered via #ComponentScan, #Controllers will get mapped etc.
The XML equivalent here would be placing xml configuration files somewhere to your classpath (META-INF folder being obvious choice) and import them all using wildcard. The idea is the same. If the plugin jar file is on classpath you will get the xml file imported and the beans (controllers, ...) will get loaded.
There are drawbacks to this approach like the modules not being isolated but its definitely option for simpler applications.
You can find a sample spring boot web project here.
By dynamically loading jars I assume you want to add dependencies to your project. For this you can update pom.xml of the sample project and put your dependencies here.

How to migrate from traditional java web application (with web.xml) to spring boot?

I wanna switch my projects to spring-based product.
My first step is to transform my java web application from a generated WAR file, to a standalone executable jar, powered by spring boot.
Let's take a open source web application example from github.: Vaadin-Spring Web Application
The web.xml file can be found here.
The root-context file can be found here.
I hope that there are some guides for me to perform the transformation.
I have also submit an issue in the spring-boot project.
This application is not a Spring MVC application as far as I can tell - it would probably be a lot easier to migrate if it was. The goal (per the github issue) is to obtain an executable JAR. The basic plan though might be to migrate first to a WAR using Spring Boot and then to a JAR once that is working. It's a pretty simple app so all we really need to do is look at the web.xml and translate it into the relevant Spring Boot features. Here are some general guides:
Create a deployable WAR by extending SpringBootServletInitializer (e.g. in a class called Application), and add the Spring Boot #EnableAutoConfiguration annotation. Example:
#Configuration
#EnableAutoConfiguration
#ComponentScan
public class Application extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
Then add some configuration:
A #Bean of type Servlet or ServletRegistrationBean installs that bean in the container as if it was a <servlet/> and <servlet-mapping/> in web.xml
A #Bean of type Filter or FilterRegistrationBean behaves similarly (like a <filter/> and <filter-mapping/>).
The ApplicationContext in this case is rooted in an XML file, so the easiest first step is to #Import that into the Spring Application. This one is so simple that it can be recreated in a few lines as #Bean definitions.
Static resources can be moved to /public (or /static or /resources or /META-INFO/resources) in the classpath root
Once the WAR is working we make it executable by adding a main method to our Application, e.g.
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
See also the Getting Started Guide on Converting a JAR to a WAR.
As I said, the biggest problem with this specific app is that it isn't a Spring MVC app. As the Irishman might say "If I wanted to get to there, sir, I wouldn't be starting from here." This is an interesting question in general, but I recommend anyone else looking to migrate a Spring application to Spring Boot read the general advice here but maybe start another discussion somewhere else.
Anyway, I'll have a bash at converting this specific app (source code jars would be nice), and update this response if I learn anything new.

Resources