SETUP
I have one jar (say its A) which adds another jar (say its B) as its dependency through Maven. Jar A is from Spring Boot Application.
ISSUE
I have properties file in jar B under its path src/conf/. Am trying to set the path value in the value attribute of #PropertySource in one of java file in jar B. When trying to do it, it refers the src/conf of jar A. How can i achieve this.
#PropertySource(
value = {
"file:${spring.profiles.path}other-services-dev.properties" })
Here spring.profiles.path gets its value from the deployment script of jar A
Use "classpath:" instead of file:.
Make the property file a resource of jar B by moving it to src/main/resources. Then you can reference the file like this: #PropertySource("classpath:/other-services-dev.properties").
The better approach, which has better encapsulation, would be to define a class in jar B, which is annotated #PropertySource("classpath:/other-services-dev.properties"), and exposes the properties through getters. Then jar A can simply let this class be injected.
In jar B:
#PropertySource("classpath:/other-services-dev.properties")
#Service
class BProperties
#Value("${setting1}")
private String setting1;
public String getSetting1() {
return setting1;
}
}
In jar A
#Service
class SomeService {
#Autowired
private BProperties properties;
}
Related
I have my custom properties defined in application.properties file located in src/main/resources folder. I want to add test cases for my application hence I added application-integration.properties in the src/test/resources folder as mentioned here. The test case is added as below
#SpringBootTest
#ActiveProfiles("integration")
public class ServiceATest {
#Value("${app.api.url}")
private String apiUrl;
#Test
public void testService() {
Assertions.assertNotNull(apiUrl);
}
}
The above test case fails. I do not understand why the properties are not read from application-integration.properties and nor from application.properties. The latter is not expected though still just for the understanding. Any idea what could be going wrong?
Thanks.
I have created a myApp.properties in resources folder location and mentioned the server.port in this file.
myApp.properties
myApp.server.port=8020
Now I want to read load this property into my application. But I have to read this before I actually a server.
Here I am trying to do like this
#SpringBootApplication
#ComponentScan(basePackages = {"com.myorg.myapp" })
#EnableConfigurationProperties
#PropertySource("classpath:myApp.properties")
#Component
public class MyAppApplication {
#Value("${myApp.server.port}")
private static String serverPort;
public static void main(String[] args) throws Exception{
try {
SpringApplication appCtxt = new SpringApplication(MyAppApplication.class);
appCtxt.setDefaultProperties(Collections
.singletonMap("server.port", serverPort));
appCtxt.run(args);
} catch (Exception e) {
e.printStackTrace();
}
}
But serverPort is coming as null.
I also tried to create a separate Config file like this but it can't be accessed in static main
#Configuration
#PropertySource("myApp.properties")
#ConfigurationProperties
public class MyAppConfig {
#Value("${myApp.server.port}")
private String serverPort;
/**
* #return the serverPort
*/
public String getServerPort() {
return serverPort;
}
}
Any suggestion would be helpful.
Spring boot injects properties during the initialization of the application context.
This happens (gets triggered) in the line:
appCtxt.run(args);
But you try to access the property before this line - that why it doesn't work.
So bottom line, using "#Value" in the main method doesn't work and it shouldn't.
Now from the code snippet, it looks like you could merely follow the "standards" of spring boot and create the file application.properties with:
server.port=1234
The process of starting the embedded web server in spring boot honors this property and bottom line it will have the same effect and Tomcat will be started on port 1234
Update 1
Based on OP's comment:
So, how can I have multiple application.properties.
In the Spring Boot's documentation it is written that application.properties are resolved from the classpath. So you can try the following assuming you have different modules A,B,C and web app D:
Create src/main/resources/application.properties inside each of 4 modules and pack everything together. The configuration values will be merged (hopefully they won't clash)
If you insist on naming properties A.properties, B.properties and C.properties for each of non-web modules, you can do the following (I'll show for module A, but B and C can do the same).
#Configuration
#PropertySource("classpath:A.properties")
public class AConfiguration {
}
Create in Module A: src/main/resources/A.properties
If you need to load the AConfiguration automatically - make the module A starter (using autoconfig feature of spring-boot):
Create src/resources/META-INF/spring.factories file with the following content:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
<package_of_AConfiguration>.AConfiguration
Also this has been the requirement to separate C from entire bundle where it might run as bundle for some and as a separate for some others
Although I haven't totally understood the requirement, but you can use #ConditionalOnProperty for configuration CConfiguration (that will be created just like AConfiguration.java in my previous example) but this times for module C.
If the conditional is met, configuration will run and load some beans / load its own properties or whatever. All in all conditionals (and in particular Profiles in spring) can help to reach the desired flexibility.
By default, the application.properties file can be used to store property pairs, though you can also define any number of additional property files.
If you save myApp.server.port=8020 in application.properties, it will work fine.
To register a custome property file, you can annotate a #Configuration class with the additional #PropertySource annotation:
#Configuration
#PropertySource("classpath:custom.properties")
#PropertySource("classpath:another.properties")
public class ConfigClass {
// Configuration
}
make sure, your class path is correct.
I have a hard time understanding how application context work in multi module project.
Lets say I have a WAR Application "App". App has Jars A, B and C. A has a pom dependency on B and C has pom dependency on B. So can the beans defined in A be used in C as they are packaged in WAR file and are loaded at run time.
The application-context files of A, B and C are imported in WAR application.
Yes it is possible. Here is why:
Simply because Spring is able to scan the classpath of the your application (if you are using the java annotation, #ComponentScan('com') will load every class you have in the package com.**).
If you are only running your JAR C with its dependencies, and you are loading every class in a particular package for instance, then you will be fine, you won't have any surprises.
The same if you do A and B.
But if you use A, B and C, and you have Spring scanning your packages (some common packages between A and C for instance), then you can have beans declared by A and beans declared by B. Which could some error if you thought you were having only one bean of type Datasource, by you have one in A and one in C.
Here is an example:
A has a DataSource.
package com.my.datasource;
#Configuration
public class MyConfigurationA() {
#Bean
public DataSource datasource() {
//return new datasource...
}
}
C has a DataSource.
package com.my.datasource;
#Configuration
public class MyConfigurationC() {
#Bean
public DataSource datasource() {
//return new datasource...
}
}
And B declared a JdbcTemplate :
package com.my.template;
#Configuration
public class MyConfigurationB() {
#Bean
public JdbcTemplate template(DataSource ds) {
return new JdbcTemplate(ds);
}
}
If you run:
- C + B -> 1 DataSource + 1 JdbcTemplate. OK !
- A + B -> 1 DataSource + 1 JdbcTemplate. OK !
- A + C + B -> -> 2 DataSources + 1 JdbcTemplate. KO !
This situation will happen if the WAR you deploy also has a #ComponentScan("com.my").
So yes, it is possible to have access of bean defined in A in C, but this can be like hidden dependencies, you code is unclear, the execution is uncertain and could fail like succeed without you knowing what is going on.
Is it clearer ?
I have the following maven project structure:
eu.arrowhead
common
repository
-AJpaRepository.class
orchestrator
controller
-AController.class
OrchestratorApplication
other_modules...
Where two of the modules are common, and orchestrator. Common is a dependency for the Orchestrator module. The JpaRepositoryClass is annotated with #Repository.
In the controller class I use the constructor autowiring to get a copy of the repository:
private final AJpaRepository serviceRepo;
#Autowired
public AController(AJpaRepository serviceRepo){
this.serviceRepo = serviceRepo;
}
And finally, in the Application class, I use scanBasePackages, to pick up the components from the common module:
#SpringBootApplication(scanBasePackages = "eu.arrowhead")
public class OrchestratorApplication {
public static void main(String[] args) {
SpringApplication.run(OrchestratorApplication.class, args);
}
}
When I start the application, I get:
Description:
Parameter 0 of constructor in eu.arrowhead.orchestrator.controller.ArrowheadServiceController required a bean of type 'eu.arrowhead.common.repository.ArrowheadServiceRepo' that could not be found.
Action:
Consider defining a bean of type 'eu.arrowhead.common.repository.ArrowheadServiceRepo' in your configuration.
If I use scanBasePackages = {"eu.arrowhead.common"} then the application starts without an error, but I can not reach the endpoint in my controller class (getting the default 404 error). If I write scanBasePackages = {"eu.arrowhead.common", "eu.arrowhead.orchestrator"} it's the same as if only "eu.arrowhead" is there, I get the same error at startup.
Is this how this supposed to work? I highly doubt it.
Depencendies:
Common module: starter-data-jpa, starter-json, mysql-connector-java, hibernate-validator
Orch module: starter-web, the common module.
I also tried using #ComponentScan, but had the same result. What is the problem? Thanks.
You are missing #EnableJpaRepositories("eu.arrowhead") annotation to enable Spring Data JPA repository scanning.
I have two plugins that are using same duplicated methods. I want to refactor then and move that methods to a shared class and host it in a dependency jar.
Those method required some maven properties as MavenProject, PluginDescriptor and some others maven classes that used to be injected into the mojo.
Is there anyway of such properties to be injected directly in the shared class?
or do I need to inject them into the mojo and then call some initialization method?
I declared the shared class with #Named and created a constructor with #Inject. The mojo class has a contructor too (the code below).
Then I tried run it. All the values are being inject into the mojo properly but the shared class object inner properties values are null.
#Inject
public SharedValidationHelperDefault(final MavenProject project,
final BuildContext buildContext,
final RuntimeInformation runtimeInformation, final Log log) {
this.buildContext = buildContext;
this.project = project;
this.runtimeInformation = runtimeInformation;
this.log = log;
}
...
#Inject
public AbstractContainerPackMojo(
final RuntimeInformation runtimeInformation,
final MavenProjectHelper projectHelper,
final BuildContext buildContext, SharedValidationHelper validationHelper) {
this.validationHelper = validationHelper;
this.buildContext = buildContext;
this.runtimeInformation = runtimeInformation;
this.projectHelper = projectHelper;
}
There's a difference between Mojo annotations and Component/JSR330 annotations: they have different purposes and are differently injected. However, it is possible to have an abstract/shared Mojo. Have a look at the surefire project: both the surefire-plugin and failsafe-plugin have some shared Mojo code&configuration in a separate module ( http://maven.apache.org/surefire/maven-surefire-common/index.html )