Spring #Value not getting set - spring

I'm sure this has been asked 1000 times, because i've seen them, however I'm missing something.
Context:
<beans profile="localDev">
<util:properties id="propertiesLocalDev"location="classpath:/localDev.properties"/>
</beans>
<beans profile="test">
<util:properties id="properties-test" location="classpath:/test.properties"/>
</beans>
Init:
System.setProperty("spring.profiles.active", "localDev");
GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();
ctx.load("classpath:applicationContext.xml");
ctx.refresh();
Config:
#Configuration
public class AppConfig {
#Value("${test.value}")
private String testValue;
...
Logs:
INFO: Loading properties file from class path resource [localDev.properties]
Properties:
test.value=ugh
So it seems the properties are getting read, however the the value in AppConfig.testValue is not getting set. I have tried pure java java/xml etc... Some configs break some work, tried using #PropertySource, but the constant is testValue never gets set, so I'm fundamentally doing something wrong.
The overall goal is to load different properties files depending on different profiles.
Can anyone see what I'm doing wrong?
Thanks

You will also a need a PropertySourcesPlaceholderConfigurer which can resolve the property for you. This is configured using:
<context:property-placeholder location="..."
local-override="true" properties-ref="propertiesLocalDev" />
With this your property value should resolve cleanly.
This also should work - using Spring-EL:
#Value("#{#propertiesLocalDev['test.value']}")
private String testValue;

Try
public class AppConfig {
#Autowired
private String testValue;
}
If the variable is properly autowired you can use
String sUgh = testValue.getProperty("test.value"); // = "ugh"
I alsou would use plain
<util:properties id="propertiesLocalDev"location="classpath:/localDev.properties"/>
instead of using the
<beans profile>
tag.

You have to use something like, in XML only use following
<util:properties id="test" location="classpath:fn-test-configuration.properties" />
Now, following way you can use the property values in class
#Value("#{test.test.value}")
private String testValue;
I have used the same way and it is working fine.

Related

What configuration enables the evaluation of #Value annotations?

I'm tying to do a very minimal programmatic/annotation based configuration of Spring, to do some command line stuff and I want to be able to inject value of some bean values from System properties.
I'm using the #Value like this:
#Value("${MigrateDb.task:default}")
private String task;
It's sort of working, but it's not evaluating the value definition, I'm just getting "${MigrateDb.task:default}" in the actual field, instead of Spring evaluating it and giving me the value of the Migrate.db.task system property (or default).
What do I need to add to my Configuration class to enable this behaviour?
try using it this way:
#Value("${MigrateDb.task:default}")
private String task;
XML Config:
<context:property-placeholder
location="your.filelocation.properties" />`
Java Config :
#Bean
public static PropertyPlaceholderConfigurer propertyPlaceholderConfigurer() {
PropertyPlaceholderConfigurer propertyPlaceholderConfigurer = new PropertyPlaceholderConfigurer();
propertyPlaceholderConfigurer.setLocation(new ClassPathResource("file.properties"));
return propertyPlaceholderConfigurer;
}
From ShadowRay's answer, the minimum code to enable the requested behaviour is:
#Bean
public static PropertyPlaceholderConfigurer propertyPlaceholderConfigurer(){
return new PropertyPlaceholderConfigurer();
}
Method should be static as per: https://stackoverflow.com/a/14943106/924597

Using #Value to inject property to constructor, SpelEvaluationException: EL1008E (4.1.6)

I read a post about authentication with facebook, http://www.jasha.eu/blogposts/2013/09/retrieve-facebook-profile-data-java-spring-social.html
he use autowired to inject 3 arguments to the constructor. which gives me SpelEvaluationException.
My project, I add a config.properties under /src/
facebook.clientId=237473571343
facebook.clientSecret=9iuwijig[sa[w90u2tjgjgj
application.host=http://localhost:8080
and the constructor
#Controller
public class FacebookSpringSocialAuthenticator {
public static final String STATE = "state";
private String applicationHost;
private FacebookConnectionFactory facebookConnectionFactory;
#Autowired
public FacebookSpringSocialAuthenticator(
#Value("#{properties['facebook.clientId']}")
String clientId,
#Value("#{properties['facebook.clientSecret']}")
String clientSecret,
#Value("#{properties['application.host']}")
String applicationHost) {
this.applicationHost = applicationHost;
facebookConnectionFactory =
new FacebookConnectionFactory(clientId, clientSecret);
}
}
after search and reading discussions and docs, I still don't figure out what to do.
the #{} is spring EL support, don't know how to use it, and don't know the difference to ${}
if I change to #Value("${facebook.clientId}"), there will be no exception, then I use debug mode to read the value of clientId, it does not show 237473571343, it shows ${facebook.clientId}, is that working correctly?
#Value("#{properties['facebook.clientId']}")
In your #Value annotation you are using a SpEL expression. In your case it is going to look for a Map or Properties object named properties and try to find a property with the key facebook.clientId.
To make it work you need to add the following
<util:properties id="properties" location="config.properties" />
Although this works I would strongly suggest to use a *PlaceHolderConfigurer instead of using SpEL.First add atag to your configuration, next change your#Value` to simply use properties.
<context:property-placeholder location="config.properties" />
Then your #Value can be like
#Value("${facebook.clientId}")
The added advantage of this is that you could also use system or environment properties to do some configuration (or override parts of your configuration).

How do #value annotations work in Spring?

I've never worked with Spring before, and I've run into a configuration object that looks somewhat like this
public class Config {
#Value("${app.module.config1}")
private String config1;
#Value("${app.module.config2}")
private String config2
...
public String getConfig1() {
return config1;
}
...
Can anyone explain what is happening here? I'm assuming this is some type of code injection, but I can't find where these values are coming from!
They allow you to direct inject a Value from a properties file (system or declared property) in the variable. Using the util:properties tag you can add something like this in your applicationContext.xml
<util:properties id="message" location="classpath:com/your/program/resources/message.properties" />
Pointing for a properties file named "message.properties" with some content:
application.hello.message = Hello World!
And then, in your java source file, inject a direct value from this properties file using the #Value annotation:
#Value("#{message['application.hello.message']}")
private String helloWorldMessage;
#Value("${app.module.config1}")
This is part of the spring expression language where the spring framework would look for app.module.config1 JVM property from System.getProperties() and injects the value of that property into config1 attribute in that class. Please see this reference for more details in Spring 3.0.x and this reference for the current docs.

Reading a "properties" file

I have a Rest service using Resteasy (running on top of Appengine), with the following psuedo code:
#Path("/service")
public class MyService {
#GET
#Path("/start")
public Response startService() {
// Need to read properties file here.
// like: servletContext.getResourceAsStream("/WEB-INF/config.properties")
}
}
However its obvious that the servlet context cannot be accessed here.
And code like:
InputStream inputStream = this.getClass().getClassLoader()
.getResourceAsStream("/WEB-INF/config.properties");
Can't be executed within the Appengine environment.
EDIT:
I have tried doing it with Spring like:
appContext.xml
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations" value="/WEB-INF/auth.properties"/>
</bean>
Then, put this on the actual class fields:
#Path("/service")
public MyService{
#Autowired
#Value("${myservice.userid}")
private String username;
#Autowired
#Value("${myservice.passwd}")
private String password;
// Code omitted
}
However, part of the code of the MyService complains because the username and password was not "injected", I mean its empty although its on the auth.properties file
In RESTEasy you can easily inject Servlet context via #Context annotation: http://docs.jboss.org/resteasy/docs/2.3.1.GA/userguide/html_single/index.html#_Context
Examples can be found here: Rest easy and init params - how to access?
This should work if you put the file in /WEB-INF/classes/ (which, importantly, is on the classpath), specifying config.properties as a file at the top-level.
this.getClass().getClassLoader().getResourceAsStream("/config.properties");
See this similar question: How to load properties file in Google App Engine?
Edit: Now you've edited, I'll respond & answer the Spring-related question. So, put the auth.properties into /WEB-INF/classes/ , and then specify classpath as follows.
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:auth.properties"/>
</bean>

Spring + Mockito test injection

My question is very similar to the issue raised in Injecting Mockito mocks into a Spring bean. In fact, I believe the accepted answer there might actually work for me. However, I've got one issue with the answer, and then some further explanation in case the answer there is not in fact my answer.
So I followed the link in the aforementioned post to the Springockito website. I altered my test-config.xml to include something similar to the following:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mockito="http://www.mockito.org/spring/mockito"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.mockito.org/spring/mockito http://www.mockito.org/spring/mockito.xsd">
...
<mockito:mock id="accountService" class="org.kubek2k.account.DefaultAccountService" />
...
</beans>
There seems to be something wrong with the www.mockito.org redirect currently, so I found the XSD code at https://bitbucket.org/kubek2k/springockito/raw/16143b32095b/src/main/resources/spring/mockito.xsd and altered the final entry in xsi:schemaLocation to point to this bitbucket link.
Running mvn test then produced the following error (newlines added for readability):
Caused by: org.springframework.beans.factory.xml.XmlBeanDefinitionStoreException:
Line 43 in XML document from class path resource [spring/test-context.xml] is invalid;
nested exception is org.xml.sax.SAXParseException; lineNumber: 43; columnNumber: 91;
The prefix "mockito" for element "mockito:mock" is not bound.
So the question regarding Springockito is: Is it possible anymore to include this? What am I missing?
Now, on to the further explanation...
I have an interface whose implementation I'm trying to test:
public interface MobileService {
public Login login(Login login);
public User getUser(String accessCode, Date birthDate);
}
The implementation contains a DAO that Spring #Autowires in for me:
#Service
public class MobileServiceImpl implements MobileService {
private MobileDao mobileDao;
#Autowired
public void setMobileDao(MobileDao mobileDao) {
this.mobileDao = mobileDao;
}
}
I don't want to alter my interface to include a setMobileDao method, because that would be adding code just to support my unit testing. I'm trying to mock out the DAO since the actual SUT here is the ServiceImpl. How can I achieve this?
You don't want to test your interface: it contains no code at all. You want to test your implementation. So the setter is available. Just use it:
#Test
public void testLogin() {
MobileServiceImpl toTest = new MobileServiceImpl();
toTest.setMobileDao(mockMobileDao);
// TODO call the login method and check that it works as expected.
}
No need for a spring context. Just instanciate your POJO service, inject mock dependencies manually, and test the methods you want to test.
After struggling with the Springockito XSD issue, for a while, I found a much simpler solution. Let Spring inject the mock for you using a factory method, i.e. in applicationContext.xml put:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean class="org.mockito.Mockito" factory-method="mock">
<constructor-arg value="com.gerrydevstory.mycoolbank.AccountsDAO"/>
</bean>
<bean class="com.gerrydevstory.mycoolbank.BankingService"/>
</beans>
where the AccountsDAO bean is injected into the BankingService class. The corresponding JUnit test case is:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration("/BankingServiceTest.xml")
public class BankingServiceTest {
#Autowired private BankingService bankingService;
#Autowired private AccountsDAO mockAccountsDAO;
#Test
public void testTransfer() throws Exception {
// Setup 2 accounts
Account acc1 = new Account();
acc1.setBalance(800.00);
Account acc2 = new Account();
acc2.setBalance(200.00);
// Tell mock DAO to return above accounts when 1011 or 2041 is queried respectively
when(mockAccountsDAO.findById(1011)).thenReturn(acc1);
when(mockAccountsDAO.findById(2041)).thenReturn(acc2);
// Invoke the method to test
bankingService.transfer(1011, 2041, 500.00);
// Verify the money has been transferred
assertEquals(300.00, acc1.getBalance(), 0.001);
assertEquals(700.00, acc2.getBalance(), 0.001);
}
}
Personally I find this very elegant and easy to understand. For more details, see the original blog post.
You have three options to set your mock dao:
Test the implementation - which gives a seam for your mock via the setDao method. (as JB's answer)
Add the setDao method to the interface - not desired since you don't want to add code just to support your tests.
Add a constructor to the impl class to accept the dao - not desired for same reason as #2.
If you wanted to do #3, you'll need to add a constructor to the MobileService that accepts the MobileDao.
public MobileServiceImpl(MobileDao mobileDao) {
this.mobileDao = mobileDao;
}
Then your test will look like this:
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.*;
import java.util.Date;
import org.junit.Before;
import org.junit.Test;
public class MobileServiceImplTest {
private MobileService systemUnderTest;
private MobileDao mobileDao;
#Before
public void setup() {
mobileDao = mock(MobileDao.class);
systemUnderTest = new MobileServiceImpl(mobileDao);
}
#Test
public void testGetUser() {
//if you need to, configure mock behavior here.
//i.e. when(mobileDao.someMethod(someObject)).thenReturn(someResponse);
systemUnderTest.getUser("accessCode", new Date());
verify(mobileDao).getUser("JeffAtwood");
}
}
Please note that you have not provided us with the details of the MobileDao so I created a getUser method that accepts a String.
To make the test pass, your MobileServiceImpl would just need this:
mobileDao.getUser("JeffAtwood");
The problem looks like your classpath doesn't contain actual springockito jar - You don't have to change the URL's - these are only the tokens that are used internally by spring - they are not being resolved - all You need is a new enough Spring distribution and springockito on classpath.
Kuba (creator of aforementioned lib :) )
I had same problem, I wanted use springockito by according their wiki but validations xml throw errors. So when i tried go to locations where xsd supposed to be, that hasn't. So i read this and get to working with this:
xmlns:mockito="http://www.mockito.org/spring/mockito"
xsi:schemaLocation="http://www.mockito.org/spring/mockito
https://bitbucket.org/kubek2k/springockito/raw/16143b32095b/src/main/resources/spring/mockito.xsd">
But when i looking at this link, had bad feeling. It seems to me that's not permanent stable link (you must check if i wrong)

Resources