I want to generate a java jar which when included on the classpath of another project will launch a periodic task that does something in the background.
This is very similar to eureka client. You include the dependency and add an annotation after which a service is started automatically to poll eureka server.
How can I do that?
Edit: I got it to work using maven, following the example provided in the comments
github.com/shauank/spring-boot/tree/master/client (client which is having taskexecutor)
github.com/shauank/spring-boot/tree/master/application (Application which uses jar created in step1)
You can use concept of Autoconfiguration. Same is used by Eureka and Config server.
Under src/main/resource create spring.factories and add following entry
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
location.to.your.executor
Your class,
pacakage location.to.your.executor
class MyExecutor{
public MyExecutor(){
//Your code for task executor
}
}
Now, above code can be build as jar and included into another spring boot project.
So, when you run another jar, spring boot will look for auto configuration onto spring.factories class and load classes defined into it.
Related
We are trying to use spring-test's SpringExtension to write integration tests for our Spring and Hibernate-based Tomcat web application. Our sessionFactory bean configuration has the property configured mappingJarLocations with a sample value as /WEB-INF/lib/company-common*.jar which contains hibernate mapping files. In both actual deployment and Eclipse dev deployment, this works fine as the docBasePath (in Servlet environment) is appended to this pattern and the files are getting resolved. But this is not the case while running JUnit test cases either in a local or a CI environment.
We tried our best to use the provided support by having few overridden implementations of WebTestContextBootstraper, GenricXmlWebContextLoader, XmlWebApplicationContext, and WebDelegatingSmartContextLoader but had to finally give up as we cannot override the final method org.springframework.test.context.web.AbstractGenericWebContextLoader.loadContext(MergedContextConfiguration) to provide the custom implementation of XmlWebApplicationContext. Our current approach is to manually create the application context and use it in the tests.
Here is the project structure:
Project_WebApp
|--src/**
|--WebContent/**
|--pom.xml
When the app is packaged as Project_WebApp.war, the dependencies are inside WEB-INF/lib from the root of extracted war. When deployed as a webapp in Tomcat using Eclipse, the dependencies are copied to <Eclipse_Workspace_Dir>/.metadata/.plugins/org.eclipse.wst.server.core/tmp0/Project_WebApp/WEB-INF/lib. In both cases, the dependencies are available at <Resource_Base_Path>/WEB-INF/lib and Resource_Base_Path has no relation to Project_WebApp base directory.
Questions:
Did any one use SpringExtension in a scenario similar to above? If so can you suggest any alternative approaches?
Instead of /WEB-INF/lib/company-common*.jar, we tried a classpath-based pattern but didn't work as the obtained class path resources don't match the pattern. Is there anything else to try here?
Let say I have an external jar named data-access-0.0.1.jar that contains Spring annotation like #Component, #Bean. But this jar does NOT contain the main method to run as a Spring application (means no #SpringBootApplication, no #ComponentScan, ...).
Now I have another jar named employee.0.0.1.jar (does have the main method to run as Spring boot application - #SpringBootApplication), that use data-access-0.0.1.jar as a dependency. But somehow it does not scan #Bean, #Component in an external jar (error when starting the app, no bean with type "myComponent" found).
I think #ComponentScan in employee-0.0.1.jar should configure base packages include a package from the external jar and it should work, but I do not want to apply this mechanism. I want to somehow configure in the external jar so that any another jar that depend on it should scan the whole jar for autowiring
I have two spring boot apps. The first is an API library that's pulled into the second (a web application) as a dependent jar.
The first, is an API library that houses functions to create "cases" in an IBM solution. It's a standalone type jar that has a service class that exposes methods like getCaseXMLForDocId(String docId) or createCaseForAgreementNumber(String agreementNumber)
The first library called CaseInvocationAPI has an application.properties file which has several properties. For example:
caseinvocation.query.fetchNonProcessedCaseXml=SELECT Id, CaptureSource, AgreementNumber, CaptureSourceID FROM CaseInvocation WHERE ProcessIndicator IN (0, 2)
The service class has a method which makes a query, grabbing that query string from a member variable that's populated with a property from the application.properties file:
#Value("${caseinvocation.query.fetchNonProcessedCaseXml}")
private String selectNonProcessedQueryString;
The second SpringBoot app is a webapplication that has REST controllers. These controllers expose endpoints that call the CaseInvocationAPI library, specifically the CaseInvocationService class.
The problem I am having is that when the SpringBoot WEBAPPLICATION starts up, the context configuration blows up with the following error:
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'caseinvocation.query.fetchNonProcessedCaseXml' in string value "${caseinvocation.query.fetchNonProcessedCaseXml}"
at org.springframework.util.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:174)
at org.springframework.util.PropertyPlaceholderHelper.replacePlaceholders(PropertyPlaceholderHelper.java:126)
at org.springframework.core.env.AbstractPropertyResolver.doResolvePlaceholders(AbstractPropertyResolver.java:219)
at org.springframework.core.env.AbstractPropertyResolver.resolveRequiredPlaceholders(AbstractPropertyResolver.java:193)
at org.springframework.context.support.PropertySourcesPlaceholderConfigurer$2.resolveStringValue(PropertySourcesPlaceholderConfigurer.java:172)
at org.springframework.beans.factory.support.AbstractBeanFactory.resolveEmbeddedValue(AbstractBeanFactory.java:813)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1039)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1019)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:566)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:349)
... 45 common frames omitted
It appears that when the WebApp starts up, when it's trying to build the classes from the dependent jar, those properties are not being found.
I didn't think I had to copy each and every property out of the dependent jar application.properties file into an application.properties file in the Webapp project.
Why isn't the WebApp project (CaseInvocationWebApp) picking up the application.properties file from the dependent jar file (CaseInvocationAPI)?
I checked the compiled jar file (CaseInvocationAPI) and the application.properties file is there in the jar.
Looks like the problem was related to the fact that both the child jar and the webapp have application.properties files. I wasn't aware that the parent WebApp application.properties sort of overwrites the others (ignoring all others really).
Special thanks to Paschoal for his response.
You can see details on the answer here:
Adding multiple application.properties files
There are 3 ways (that I can think of) you can approach this:
The dependency, API library, should not have an application.properties since it's a library and not an executable Spring boot application in itself. You only define the properties in your web application's application.properties, even for the API library.
But, here the assumption is that you have access to the API library jar.
You can redefine all the properties in web application's application.properties essentially overriding them.
Explicitly configure the Spring boot application to use both the application.properties files, each for different set of properties.
Caveat: The file names must be different, as config location is classpath for both.
#SpringBootApplication
public class WebApplication {
public static void main(String[] args) {
SpringApplication app = new SpringApplicationBuilder(WebApplication.class)
.properties("spring.config.location=classpath:api-application.properties,classpath:application.properties")
app.run(args);
}
}
I am learning Spring Boot. I made a simple Spring Boot project that can output a hello world string at http://localhost:8080/welcome
I use Maven to build my project that would output a jar file.
To start up my spring boot app, I use the command as below
java -jar my-springboot-app.jar
My question is:
How is java smart enough to locate my main class and its main method (e.g. the application launcher)?
I checked the jar file and browsed those BOOT-INF & META-INF and could not find any clues.
Does the spring boot framework (#SpringBootApplication) or maven automatically do the magic for me?
In case of spring boot jar the things are little bit more complicated than regular jar. Mainly because spring boot applicaton jar is not really a JAR (by jar I mean something that has manifest and compiled classes). Regular JARs can be "recognized" and processed by jvm, however in Spring Boot there are also packed dependencies (take a look at BOOT-INF/lib) so its jars inside JARs. How to read this?
It turns out that spring boot always has its own main class that is indeed referred to in MANIFEST.MF and this a real entry point of the packaged application.
The manifest file contains the following lines:
Main-Class: org.springframework.boot.loader.JarLauncher
Start-Class: com.example.demo.DemoApplication
Main-Class is a JVM entry point. This class, written by spring developers, basically does two things:
- Establishes a special class loader to deal with a "non-regular-jar" nature of spring boot application. Due to this special class loaders spring boot application that contains "jars" in BOOT-INF/lib can be processed, for example, regular java class loaders apparently cannot do this.
- Calls the main method of Start-Class value. The Start-Class is a something unique to spring boot applications and it denotes the class that contains a "main" method - the class you write and the class you think is an entry point :) But from the point of view of the spring boot infrastructure its just a class that has an "ordinary" main method - a method that can be called by reflection.
Now regarding the question "who builds the manifest":
This MANIFEST.MF is usually created automatically by plugins offered by Spring Developers for build systems like Maven or Gradle.
For example, the plugin looks like this:
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
During its work, this plugin identifies your main class (com.example.demo.DemoApplication in my example). This class is marked with #SpringBootApplication annotation and has a public static void main method.
However, if you put many classes like this the plugin probably won't recognize the correct class so you'll need to configure the plugin properties in POM.xml to specify the right class.
Java classes are executed within a larger context,
you run java -jar somejar.jar the class in question will be selected in the .jar file's manifest.
#SpringBootApplication will have componentscan, enabling auto configuration(autowired)
componentscan - to identify all the controller, service and configuration classes within the package.
I'm having a problem properly setting up spring boot for my multi-module maven project.
There is a module "api" that uses another module "core". Api has an application.properties file that contains spring.mail.host=xxx. According to the spring boot documentation this provides you with a default implementation of the JavaMailSender interface, ready to be autowired.
However the class that is responsible for sending out the e-mails resides in the "core" package. When I try to build that module the build fails because no implementation of JavaMailSender can be found.
My guess then was that the mailing config should reside in "core" in a separate application.properties. I created that and moved the spring.mail.host property from the "api" to the "core" property file.
This time the core module builds successfully, but "api" fails to build because of the same exception, so I think I just moved the problem.
I don't understand the required structure for handling this type of situations well enough so I was wondering what the correct way is for having a "core" module containing all the correct configuration for sending mails and having other modules use the mailing code and config that resides in it.
I found the answer in another stack overflow question: How to add multiple application.properties files in spring-boot?
It turns out there can only be 1 application.properties file in the final jar that spring boot creates. To have multiple files you have to rename one of the files to something custom. I named the properties of the core module "core-application.properties".
Then in the API module I added this to the spring boot application class:
#SpringBootApplication
#PropertySource(value = {"core-application.properties", "application.properties"})
Doing this I can correctly use the base properties file and overwrite them in the more specific modules. Also you can still create profile-specific properties file (core-application-production.properties) with this setup, no need to add those to the propertysource manually). Note that #PropertySource does not work for yaml configuration files at this moment.
there is one effective application.properties per project. you just keep 2 properties file for a success build.
when api module use core module, the application.properties in core module is overwrite by api.
Your API's pom.xml must has dependency of CORE module.
the solution is to define properties files as a value of #PropertiesSource in Starter class.
but it is beter to put "classpath:" behind the properties files.
for example in Intellij idea after adding the "classpatch:" word berhind the files name, values become to link. like this:
#SpringBootApplication
#PropertySource(value = {"classpath:core-application.properties", "classpath:application.properties"})
I hope to helped you.