Is there any way to inject system variables into a Spring library? - spring

I have created a Java library that I want to call from Spring applications.
The library has some #Value parameters, which are to be populated from system variables that we pass in to the calling application via -D arguments. In addition, it has an #EnableConfigurationProperties that points to some Autowired configuration beans.
However when I call the library from a consumer application with the appropriate system variables supplied, the Spring #Value parameters do not get populated, and the beans do not get wired; they all come up null.
How can I inject the system variables from the consumer application into the Spring library classes?
Here is the main library code:
Here is the library configuration class

Spring has support for Environment.
Injecting Environment bean
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
#Component
public class MyEnvironment{
#Autowired
private Environment env;
public String getProp() {
return env.getProperty("my.app.prop");
}
}
Implement EnvironmentAware
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
#Component
public class MyEnvironment implements EnvironmentAware {
private String prop;
#Override
public void setEnvironment(Environment env) {
this.prop= env.getProperty("my.app.prop);
}
public String getProp() {
return prop;
}
public void setProp(String prop) {
this.prop= prop;
}
}

Related

Autowiring Issue with using Springboot PropertiesFactoryBean

I am new to Springboot PropertiesFactoryBean and want to inject a file from classpath so that I can populate it into a Map
Location of properties file on Eclipse: src/main/resources
contents of File: simple_property.properties:
key1=value1
key2=value2
key3=value3
My ApplicationConfiguration.java looks as below:
import org.springframework.beans.factory.config.PropertiesFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
#Configuration
public class ApplicationConfiguration {
#Bean(name = "simpleMapping")
public static PropertiesFactoryBean artifactMapping() {
PropertiesFactoryBean bean = new PropertiesFactoryBean();
bean.setLocation(new ClassPathResource("simple_property.properties"));
return bean;
}
}
I have a ApplicationRunner interface for bean to be run:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class pCli implements ApplicationRunner {
#Autowired
private SomeClass someProgram;
public static void main(String[] args) {
SpringApplication.run(pCli.class, args);
}
#Override
public void run(ApplicationArguments args) throws Exception {
someProgram.run(args.getOptionValues("param1").get(0));
}
}
I am unable to understand & proceed ahead how I can use bean to read all properties ? Example how I can get the properties into a variable of Map and access them ? (If #Autowired has already loaded the properties file from classpath then how can I access it ? )
Say you have your map in the property file like this,
app.number-map={KEY1:1, KEY2:2, KEY3:3}
You can use this value by injecting the property using the #Value annotation. Following is an example where the value is injected to a method. Spring expression #{${app.number-map}} will fetch the value in the properties file.
someMethod(#Value("#{${app.number-map}}")
Map<String, Integer> numberMap) {
// ...
}
Use application.properties since you're still learning. It'll make your life easy. Also, keeping a separate configuration bean would really help you to manage and access property values easily.

How Do I Unit Test A Jersey REST API Without Running A Server?

I am working with a REST API that is using Jersey with Spring Boot (so no specific application context in XML or Java) and Spring Data JPA.
I want to write unit tests on the GET and POST endpoints, however, I don't want to start a web server as it takes too long.
If I use JerseyTest my Spring Beans don't get loaded into the context.
public class InMemoryContainerPackageTest extends
JerseyTestNg.ContainerPerClassTest {
#Override
protected TestContainerFactory getTestContainerFactory() {
return new InMemoryTestContainerFactory();
}
#Override
public Application configure() {
ResourceConfig config = new ResourceConfig()
.register(SpringLifecycleListener.class)
.register(RequestContextFilter.class)
.register(this)
.register(MyController.class)
.packages("com.my.service");
return config;
}
If I use SpringBootTest it starts up a web server which takes about 30 seconds and ideally I want all my tests to complete in under 5 seconds otherwise developers won't run them.
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class TestNGClass1 extends AbstractTestNGSpringContextTests {
I don't think MockMvc works with Jersey endpoints.
If I use JerseyTest my Spring Beans don't get loaded into the context.
What you can do is set the property "contextConfig" in your ResourceConfig. The value will be a Spring ApplicationContext instance. So if you are using Java configuration, you would just use an AnnotationConfigApplicationContext.
#Override
public ResourceConfig configure() {
return new ResourceConfig()
.register(TestResource.class)
.property("contextConfig",
new AnnotationConfigApplicationContext(SpringConfig.class));
}
Here, SpringConfig is an arbitrary Spring #Configuration class. Below is a complete example.
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.core.Response;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.glassfish.jersey.test.inmemory.InMemoryTestContainerFactory;
import org.glassfish.jersey.test.spi.TestContainerFactory;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import static org.assertj.core.api.Assertions.assertThat;
public class SpringTest extends JerseyTest {
public static class MessageService {
public String getMessage() {
return "Hello World";
}
}
#Configuration
public static class SpringConfig {
#Bean
public MessageService service() {
return new MessageService();
}
}
#Path("test")
public static class TestResource {
#Autowired
private MessageService service;
#GET
public String get() {
return service.getMessage();
}
}
#Override
public ResourceConfig configure() {
return new ResourceConfig()
.register(TestResource.class)
.property("contextConfig",
new AnnotationConfigApplicationContext(SpringConfig.class));
}
#Override
public TestContainerFactory getTestContainerFactory() {
return new InMemoryTestContainerFactory();
}
#Test
public void testIt() {
Response res = target("test")
.request()
.get();
String msg = res.readEntity(String.class);
System.out.println(msg);
assertThat(msg).isEqualTo("Hello World");
}
}
As far as the JPA, you are going to have to configure that yourself. When using Spring Boot, all of the JPA bootstrapping is taken care of. If you are going to use Jersey Test Framework, then you are ignoring all Spring Boot configuration.
It's really not that hard to configure JPA yourself. It basically consists of configuring a DataSource, a TransactionManager, a JpaVendorAdaptor, and a LocalContainerEntityManagerFactoryBean. And to enable the Spring Data repositories, you just need to use the #EnableJpaRepositories.
Have a look at this complete example configuration.
Another thing to be wary of is that when we use the Jersey Test Framework, we will not have the test scoped transactions (and rollbacks) that you will get when working with Spring Test. So when you write your tests, you need to take this into consideration.

Autowire working in unit test but not in main java class

I've a domain class that I want to auto-populate from external config. Here is my domain class:
#Data
#Configuration
#PropertySource("classpath:application.properties")
public class StudioVo {
#Value("${studio.code}")
private code;
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
Here is my context xml:
<bean class="org.springframework.batch.core.scope.StepScope" />
<bean id="ItemReader" class="com.sdm.studio.reader.StudioReader" scope="step">
<property name="studioVo" ref="StudioVo" />
</bean>
<bean id="StudioConfigVo" class="com.sdm.studio.domain.StudioVo" />
</bean>
Here is the class where I want to use the vo:
#Slf4j
#Data
public class StudioReader implements ItemReader<List<Studio>> {
private StudioVo studioVo;
public List<Studio> read() throws Exception {
System.out.println("getCode: " + studioVo.getCode()); //code is null here
return null;
}
}
However when I run it via unit test by autowiring, it runs fine. Like this:
#RunWith(SpringRunner.class)
#SpringBootTest
public class StudioTest {
#Autowired
private StudioVo studioVo;
#Test
public void testAutoPopulationOfStudio(){
System.out.println("getCode: "+ studioVo.getCode()); // works!
// Assert.assertTrue(studioVo.getCode().equals("102"));
}
}
Not sure what's going on here - I'm working with an old Spring Batch application wrapped in Spring Boot (so there is a mix of XML based and Java based config - and may be that is the cause of this issue). What am I missing?
In your StudioTest, you are autowiring StudioReader where as you missed the #Autowired in your StudioReader code, so add it as shown below:
#Slf4j
#Data
public class StudioReader implements ItemReader<List<Studio>> {
#Autowired //add this so that studioVo can be injected
private StudioVo studioVo;
//add other code
}
Please be certain to note that using #Autowire requires a chain of Spring-managed beans below it from wherever you are using it including the class in which you are using #Autowire. That is because Spring needs the precedent references to match up the object-reference hierarchy. I.e., in business logic layer ClassA, you want to #Autowire a field. ClassA itself needs to be a managed bean. Further, if the field you want to #Autowire holds an object that has referential dependencies to other objects (and most do), these also must be Spring-managed.
For example, the following will work:
package com.example.demo;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MessageRunner {
private static SetterMessage setterMessage;
public static void main(String[] args) {
setterMessage = (SetterMessage) (new AnnotationConfigApplicationContext(DemoConfiguration.class)).getBean("setterMessage");
setterMessage.setMessage("Finally it works.");
p(setterMessage.getMessage());
}
private static void p(String s) {
System.out.println(s);
}
}
DemoConfiguration.java looks like this:
package com.example.demo;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
#Configuration
#ComponentScan(basePackages = "com.example.demo")
public class DemoConfiguration {
}
SetterMessage.java, this:
package com.example.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
#Service
#Scope("prototype")
public class SetterMessage {
private String message = null;
#Autowired
private SetterMessage2 setterMessage2;
public String getMessage(){
return message+setterMessage2.getSubMessage();
}
public void setMessage(String message) {
this.message = message;
setterMessage2.setSubMessage("("+message+")");
}
}
SetterMessage2.java:
package com.example.demo;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
#Service
#Scope("prototype")
public class SetterMessage2 {
private String subMsg = "";
public void setSubMessage(String msg) {
subMsg = msg;
}
public String getSubMessage() {
return subMsg;
}
}
Note that SetterMessage2.java is annotated as a Component (#Service) but no field in it is autowired. That is because it's the end of the object reference chain. But because it is a Component, it can be autowired into SetterMessage.java. However look at MessageRunner.java's main() method and field declarations. Note that the class field SetterMessage is NOT autowired. If it were annotated as #Autowired, main() would fail at runtime, throwing an NPE with the reference to setterMessage in main(). This is because MessageRunner.java is not marked as some kind of component. So we need to grab a valid instance of MessageSetter from the application context and use it.
To emphasize, the following version of MessageRunner.java's main() method WILL FAIL, throwing an NPE, if MessageRunner.java looked like this:
...
public class MessageRunner {
#Autowired // <-- This will not do the job for us
private static SetterMessage setterMessage;
public static void main(String[] args) {
setterMessage.setMessage("Finally it works."); // NPE here on ref to setterMessage
p(setterMessage.getMessage());
}
...
This is a real gotchya for people new to Spring. In fact, I'd place it among the Top Five Spring Newbie Discouragers and a really evil, pernicious detail that has caused new Spring programmers countless hours in aggravation and Google searches. So I do hope that noting this phenom here will save at least some newbies time and high blood pressure spikes.
Note: If you go to create the above classes in your IDE, bear in mind these were developed with Spring Boot enabled.

Loading properties using #Value into a BeanFactory object using #Bean in Spring Boot

Can anyone help me with a Spring Boot problem?
I want to create a factory bean as part of my application context but I want to be able to instantiate it with injected property values. However it seems that Spring will load FactoryBeans before anything else as demonstrated here:
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.AbstractFactoryBean;
import org.springframework.beans.factory.config.ListFactoryBean;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Bean;
#EnableAutoConfiguration
public class TestClass
{
#Value("${test.value}")
String value;
#Bean
public Object test1()
{
System.out.println("test.value=" + value );
List<String> list = new ArrayList<String>();
ListFactoryBean factory = new ListFactoryBean();
factory.setSourceList(list);
return factory;
}
public static void main(String[] args)
{
SpringApplication.run(TestClass.class, args);
}
}
When run with
java -Dtest.value=HELLO -jar myTest.jar
It loads in the value correctly:
test.value=HELLO
However, when I specify that the bean to be loaded is in fact a factory bean, and run it in the same way:
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.AbstractFactoryBean;
import org.springframework.beans.factory.config.ListFactoryBean;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Bean;
#EnableAutoConfiguration
public class TestClass
{
#Value("${test.value}")
String value;
#Bean
public AbstractFactoryBean test1()
{
System.out.println("test.value=" + value );
List<String> list = new ArrayList<String>();
ListFactoryBean factory = new ListFactoryBean();
factory.setSourceList(list);
return factory;
}
public static void main(String[] args)
{
SpringApplication.run(TestClass.class, args);
}
}
The value is null because it hasn't been injected yet.
test.value=null
Is there any way around this?
Thanks
Spring often has to query bean definitions for the type of object they produce. Factory beans are always problematic because they can cause dependency cascades in a futile attempt to resolve all dynamic information available before asking for the type.
I think ListFactoryBean is insufficiently precise about its product type (getObjectType() can only return a non-generic List.class). You might be able to write your own factory that is parameterized with the correct generic type. Or you might get away with just declaring the #Bean to return a FactoryBean<List<String>.
Another tip is to move the #Bean definition to a separate class (e.g. a nested static one) so that it can be instantiated independently of the rest of the application context. E.g.
#EnableAutoConfiguration
public class TestClass
{
protected static class NestedConfiguration {
#Value("${test.value}")
String value;
#Bean
public FactoryBean<Properties> test1()
{
System.out.println("test.value=" + value );
// ...
return factory;
}
}
...
}
Not really a Boot question this one so you might consider changing the tags.
Take look at Empowering your apps with Spring Boot's property support
There is new annotation #EnableConfigurationProperties in Spring Boot Actuator
The Spring Environment is a collection of name-value pairs taken from (in order of decreasing precedence)
1) the command line,
2) the external configuration file,
3) System properties,
4) the OS environment.
There is also possible to define application properties (external configuration) in YAML format.

Spring annotations - #Configuration to invoke spring bean auto-building

If I declare a class using #Bean and then component scan for the class, spring will instantiate the class by invoking it's constructor and injecting constructor args and injecting any fields marked with #Inject. For simplicity's sake, lets call this spring auto-building.
I dislike component scan and wish to avoid it completely (I don't wish to discuss my reasons for not liking it). I would like to use a #Configuration object instead but would still like to have the auto-building functionality available to me. Is it possible to invoke spring to auto-build my objects instead of explicitly having to pass all the constructor arguments in my #Configuration object?
Lets assume that I have a bean:
public class MyServiceImpl implements MyService {
public MyServiceImpl(Dependency1 d1, Dependency d2) { ... }
....
}
I could define a configuration object like this:
#Configuration
public class MyConfiguration {
// lets assume d1 and d2 are defined in another #Configuration
#Inject
Dependency1 d1;
#Inject
Dependency2 d2;
#Bean
public MyService myService() {
// I dislike how I have to explicitly call the constructor here
return new MyServiceImpl(d1, d2);
}
}
But now, I have explicitly had to call the MyServiceImpl constructor myself so I will have to keep updating this as my constructor changes over time.
I was hoping that I could declare an abstract method so that spring auto-building could take place:
#Configuration
public abstract class MyConfiguration {
#Bean
public abstract MyServiceImpl myService();
}
But this doesn't work. Is there a way that I can invoke spring auto-building without using a component scan?
In Google Guice, this can be done via the Binder:
https://google-guice.googlecode.com/svn/trunk/javadoc/com/google/inject/Binder.html
In Tapestry IOC, this can be done via the ServiceBinder:
http://tapestry.apache.org/ioc-cookbook-basic-services-and-injection.html#IoCCookbook-BasicServicesandInjection-SimpleServices
Update
Based on spod's answer, I was able to achieve what I was after (thanks!). Test case included for anyone that wants to do the same:
import java.util.Date;
import javax.inject.Inject;
import junit.framework.Assert;
import org.junit.Test;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
public class AutoBuildConfigurationTest {
#Configuration
public static class MyConfiguration {
#Inject
private AutowireCapableBeanFactory beanFactory;
#Bean
public Date date() {
return new Date(12345);
}
#Bean
public MyService myService() {
return autoBuild(MyService.class);
}
protected <T> T autoBuild(Class<T> type) {
return type.cast(beanFactory.createBean(type, AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR, true));
}
}
public static class MyService {
private Date date;
public MyService(Date date) {
this.date = date;
}
public Date getDate() {
return date;
}
}
#Test
public void testAutoBuild() {
ApplicationContext appContext = new AnnotationConfigApplicationContext(MyConfiguration.class);
MyService myService = appContext.getBean(MyService.class);
Assert.assertEquals(12345, myService.getDate().getTime());
}
}
The java based container configuration doesnt depend on doing a component scan in any way. Its merely a different approach for the XML based component configuration. With the XML configuration you'd just have to declare your bean with the MyServiceImpl class in case its already #inject annotated. Spring would recognize the annotations and take care of them. If you really want to instanciate MyServiceImpl from a #Configuration java class without calling the constructor yourself, then you'd have to make use of the bean factory (havent tested it, just give it a try):
#Configuration
public class MyConfiguration {
#Autowired AutowireCapableBeanFactory beanFactory;
#Bean public MyService myService() {
return beanFactory.createBean(MyServiceImpl.class, AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR, true);
}
}

Resources