EasyMock on Spring jdbcTemplate is always returning null rather mocked object - spring

I'm trying to use EasyMock 3.4 on a Java Spring project. I have successfully mocked all objects and tested the classes except a DAO which is using JDBCTemplate.
#RunWith(EasyMockRunner.class)
public class DummyDAOImplTest extends EasyMockSupport {
#TestSubject
private DummyDAOImpl dummyDAOImpl = new DummyDAOImpl ();
JdbcTemplate jdbcTemplateObject;
#Before
public void setUp(){
jdbcTemplateObject = EasyMock.createNiceMock(JdbcTemplate.class);
dummyDAOImpl.setJdbcTemplate(jdbcTemplateObject);
}
#Test
public void testGetApplicationConfigValueReturnNonNull(){
String query = "SELECT value FROM application_configuration WHERE tag=?";
String tag = "REFRESH_INTERVAL";
EasyMock.expect(jdbcTemplateObject.queryForObject(query,new Object[] {tag}, String.class)).andReturn("12");
EasyMock.replay(jdbcTemplateObject);
Assert.assertEquals(12,dummyDAOImpl.getApplicationConfigValue(tag));
}
}
public class ConfigurationDAOImpl implements ConfigurationDAO {
private JdbcTemplate jdbcTemplateObject;
#Override
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplateObject = jdbcTemplate;
}
#Override
public int getApplicationConfigValue(String tag) {
String query = "SELECT value FROM application_configuration WHERE tag=?";
String refreshTime = jdbcTemplateObject.queryForObject(query,new Object[] {tag}, String.class);
if(refreshTime != null && !"".equals(refreshTime))
return new Integer(refreshTime);
else
return 0;
}
}
Though in method testGetApplicationConfigValueReturnNonNull - I'm trying to mock it to return 12 but it always returns null.
It's the first time I'm using EasyMock. anything that I'm missing as already tried for and not able to crack it!
Best Regards,
Sundar

In fact, your only problem is your expectation line. It should be
EasyMock.expect(jdbcTemplateObject.queryForObject(eq(query), aryEq(new Object[] {tag}), eq(String.class))).andReturn("12");
By default, EasyMock will perform an equals on parameter to match the expectation. The things is that there are no equals defined for an array. So you need to specify a matcher for array (aryEq). And as soon as you have a matcher for an argument, you need to have one for all of them (for technical reasons).
The full code with some simplifications is below.
I assumed it is ConfigurationDAO that you want to test
You can replayAll since you are extending EasyMockSupport
You can use #Mock because of the runner
You do not need a nice mock. In fact, not using it here will have shown a nice exception about the unexpected call that would have been really helpful for you
The mock is now also injected by the EasyMockRunner
I prefer to add a verifyAll at the end of most of my tests. It makes sure that all expectations were used
Static imports because I think it's clearer to read
Code:
import static org.easymock.EasyMock.*;
import static org.junit.Assert.*;
#RunWith(EasyMockRunner.class)
public class DummyDAOImplTest extends EasyMockSupport {
#TestSubject
private ConfigurationDAOImpl dao = new ConfigurationDAOImpl();
#Mock
JdbcTemplate jdbcTemplateObject;
#Test
public void testGetApplicationConfigValueReturnNonNull(){
String query = "SELECT value FROM application_configuration WHERE tag=?";
String tag = "REFRESH_INTERVAL";
expect(jdbcTemplateObject.queryForObject(eq(query), aryEq(new Object[] {tag}), eq(String.class))).andReturn("12");
replayAll();
assertEquals(12, dao.getApplicationConfigValue(tag));
verifyAll();
}
}

Probably you need to load the Spring context
#ContextConfiguration(locations = "classpath:application-context-test.xml")
#RunWith(EasyMockRunner.class) public class DummyDAOImplTest extends EasyMockSupport { ...
Override your appplication-context like this
application-context-test.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context/ http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/tx/ http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd"><import resource="application-context.xml"/><bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="org.hsqldb.jdbcDriver" />
<property name="url" value="jdbc:your-db-conection" />
<property name="username" value="" />
<property name="password" value="" />

Related

Spring Advice On Setter Not Triggered

i have this below code .
advice on setter is not triggered even though setter is called.
i can see it in the console
if i do advice on String getName() everything works just fine.
but its not working on setter public void setName(String name).
spring.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<aop:aspectj-autoproxy />
<bean id="cust" class="aspectJ.Customer">
<property name="name" value="logging" />
</bean>
<bean id="aspect" class="aspectJ.LoggingAspect"/>
</beans>
Logging ASPECT
#Aspect
public class LoggingAspect {
#Before("allgetters()")
public void printAspect() {
System.out.println("Aspect Running");
}
#Before("allgetters()")
public void printAspectTwo() {
System.out.println("Aspect TWO Running");
}
#Pointcut("execution(public void setName(*))")
public void allgetters() {
}
}
CUSTOMER CLASS
package aspectJ;
public class Customer {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
System.out.println("SETTER CALLED");
}
}
Main Class
public class MainClass {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
Customer obj = (Customer) context.getBean("cust");
}
}
Your aspect signature is wrong.
* will match a singular field
.. will match zero or multiple fields
Example 1
com.stackoverflow.*.Customer.setName(..)
Matches all packages starting with com.stackoverflow and ending with Customer. The wildcard will only match one package name. The method can accept zero or more arguments. Below are three examples of what it will match:
com.stackoverflow.question.Customer.setName()
com.stackoverflow.question.Customer.setName(String arg1)
com.stackoverflow.question.Customer.setName(String arg1, String arg2)
Example 2
com..Customer.setName(*, *)
Match all packages starting with com and ending with Customer. Accept methods with two arguments of any type. Below are two examples of what it will match. Notice that the wildcard will accept any number of packages.
com.example.Customer.setName(String arg1, Object arg2)
com.stackoverflow.question.Customer.setName(Integer arg1, Double arg2)
You should change your allgetters() to the following:
#Pointcut("execution(public void setName(..))")
public void allgetters() {
}
Spring AOP only works with beans managed by Spring. A bean is not managed until it is initialized, regardless if it is defined in Java or XML.
//The Customer object returned by this method is managed.
//The Customer object within the method is not managed
#Bean
public Customer createCustomer(){
//This is a pure Java object
Customer customer = new Customer();
//The object is not yet managed. This method call will therefore never be intercepted by your Pointcut.
customer.setName(“John Doe”);
return customer;
}
Quote from the documentation:
Spring AOP only supports method execution join points for Spring
beans, so you can think of a pointcut as matching the execution of
methods on Spring beans.
Just Posting what user #javamusings said :
The advice is called only when the setter is called in the Java class. It is not called when the property is initialized in the aspectj.xml

Spring REST - binding GET parameters to nested objects

I know you can bind get request parameters to a pojo like:
#RequestMapping(value = "/reservation",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
public List<Reservation> loadReservations(ReservationCriteria criteria)
return service.loadReservations(criteria);
}
Using a pojo like:
public class ReservationCriteria {
String hotelName;
DateRange reservationDateRange;
//getters-setters omitted
}
With a request: /reservation?hotelName=myHotel
myHotel will be bound to hotelName in ReservationCriteria object.
But how can I bind parameters to the nested object DateRange? Which defined like:
public class DateRange {
Date from;
Date to;
//getters-setters omitted
}
Is there a URL pattern which allows that kind of binding something like:
/reservation?hotelName=myHotel&reservationDateRange={fromDate=14.04.2016,toDate=15.04.2016}
Or do I have to declare seperate request parameters and bind them manually?
#RequestMapping(value = "/reservation",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
public List<Reservation> loadReservations(
ReservationCriteria criteria,
#RequestParam Date from,
#RequestParam Date to)
DateRange range = new DateRange();
range.setFrom(from);
range.setTo(to);
criteria.setDateRange(range);
return service.loadReservations(criteria);
}
I would prefer not to modify ReservationCriteria class because it is used in many other projects, which would cause alot of refactoring to be made.
Since at least Spring 4 you can pass in nested objects separated with "." in the url.
In the OP case it would be for query parameters:
?reservationDateRange.from=2019-04-01&reservationDateRange.to=2019-04-03
This assumes that Date can be parsed from the given string. This may not work to an arbitrary level of nesting but I've tested it works with one additional nested object.
When you pass a POJO as container of data, Spring use the name of the properties for build the query string and with the data that you pass build the pojo through an appropriated converter. This works for planar pojo or in other words without nesting, for this purpose you have provide the your converter. for this reason you cold have a think like below:
public class ReservationCriteria {
String hotelName;
Date from;
Date to;
//getters-setters omitted
}
#RequestMapping(value = "/reservation",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
public List<Reservation> loadReservations(ReservationCriteria criteria)
return service.loadReservations(criteria);
}
/reservation?hotelName=value&from=val&to=val
in this way you can benefit of standard converter of SpringMVC.
the your attempt to use a sort of json for codificate the inner object didn't work because Spring by default in query string don't understand this presentation you have provide a converter for this purpose.
Update for answer to Ben's comment:
If you want implement a custom Converter you had implements the org.springframework.core.convert.converter.Converter<S, T> and then register the your new Converter on the Spring Conversion Service.
On xml configuration you can use FormattingConversionServiceFactoryBean and register it on mvc namespace like below:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
<mvc:annotation-driven conversion-service="conversionService"/>
<context:component-scan base-package="com.springapp.mvc"/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"/>
<property name="suffix" value=".jsp"/>
</bean>
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="converters">
<util:list>
<bean class="com.springapp.mvc.DateRangeToStringConverter"/>
<bean class="com.springapp.mvc.StringToDateRangeConverter"/>
</util:list>
</property>
</bean>
</beans>
on java config you can extends WebMvcConfigurerAdapter and add you bena like below:
#Configuration
#EnableWebMvc
public class YourWebConfigurationClass extends WebMvcConfigurerAdapter{
#Override
public void addFormatters(FormatterRegistry formatterRegistry) {
formatterRegistry.addConverter(yourConverter());
}
...
}
the your converter can be like below:
public class DateRangeToStringConverter implements Converter<DateRange,String> {
#Override
public String convert(DateRange dateRange) {
return Json.createObjectBuilder().add("fromDate",DateFormatData.DATE_FORMAT.format(dateRange.getFrom()))
.add("toDate", DateFormatData.DATE_FORMAT.format(dateRange.getTo()))
.build()
.toString();
}
}
public class StringToDateRangeConverter implements Converter<String,DateRange> {
#Override
public DateRange convert(String dateRange) {
DateRange range = new DateRange();
JsonObject jsonObject = Json.createReader(new StringReader(dateRange)).readObject();
try {
range.setFrom(DateFormatData.DATE_FORMAT.parse(jsonObject.getString("fromDate")));
} catch (ParseException e) {
e.printStackTrace();
}
try {
range.setTo(DateFormatData.DATE_FORMAT.parse(jsonObject.getString("toDate")));
} catch (ParseException e) {
e.printStackTrace();
}
System.out.println(range);
return range;
}
}
in this way you can listgening on the url: http://localhost:8080/reservation?hotelName=myHotel&reservationDateRange={"fromDate":"14.04.2016","toDate":"15.04.2016"}
pleas pay attenction on reservation DateRange field because I encoded it like a json.
I hope that it can help you

Spring Data Solr multiple cores and repository

I have apache solr with multiple cores e.g. currency, country etc... So using Spring Data Solr I can retrieve information from one core. I have got this XML configuration right now queries against 'currency' core. If I wanted to query against 'country' core how can I set this up?
<!-- Enable Solr repositories and configure repository base package -->
<solr:repositories base-package="com.acme.repository" solr-template-ref="solrCurrencyTemplate"/>
<solr:solr-server id="solrCurrencyServer" url="http://localhost:8983/solr/currency"/>
<bean id="solrCurrencyTemplate" class="org.springframework.data.solr.core.SolrTemplate">
<constructor-arg ref="solrCurrencyServer" />
</bean>
and have the repository defined as
#Repository
public interface CurrencyRepository extends SolrCrudRepository<Currency, String> {
}
and from my service I can do this
#Override
public List<Currency> getCurrencies() {
Page<Currency> currencies = (Page<Currency>) currencyRepository.findAll();
return currencies.getContent();
}
I have also tried using #SolrDocument(solrCoreName = "currency") but this din't work.
#SolrDocument(solrCoreName = "currency")
public class Currency {
public static final String FIELD_CURRENCY_NAME = "currency_name";
public static final String FIELD_CURRENCY_CODE = "currency_code";
public static final String FIELD_DECIMALS = "decimals";
#Id
#Field(value = FIELD_CURRENCY_CODE)
private String currencyCode;
//currency_name,decimals
#Field(value = FIELD_CURRENCY_NAME)
private String currencyName;
#Field(value = FIELD_DECIMALS)
private String decimals;
...
...
...
}
I need help on this asap... otherwise I will have to go back to the RestTemplate Solution :-(
Hope someone can help.
Thanks
GM
Thought I would share, We spend lot of time recently configuring multiple cores. We did in java, not xml.
As part of spring #configuration add following.
#Bean(name="solrCore1Template")
public SolrTemplate solrCore1Template() throws Exception {
EmbeddedSolrServer embeddedSolrServer = new EmbeddedSolrServer(getCoreContainer(), "core1");
return new SolrTemplate(embeddedSolrServer);
}
#Bean(name="solrCore2Template")
public SolrTemplate solrCore2Template() throws Exception {
EmbeddedSolrServer embeddedSolrServer = new EmbeddedSolrServer(getCoreContainer(), "core2");
return new SolrTemplate(embeddedSolrServer);
}
#Bean
#Scope
public CoreContainer getCoreContainer() throws FileNotFoundException{
String dir = <path_to_solr_home>;
System.setProperty("solr.solr.home", dir);
CoreContainer.Initializer initializer = new CoreContainer.Initializer();
return initializer.initialize();
}
And to use each template use like below in service classes.
#Resource
private SolrTemplate solrCore1Template;
Embedded server can be relaced with HTTP using below code.
HttpSolrServer httpSolrServer = new HttpSolrServer(getSolrURL());
return new SolrTemplate(httpSolrServer, "core1");
Hope this helps. I know it's a very late reply for the question asked.
multicore support via namespace config is unfortunately an open issue. You'll need to have a separate SolrTemplate for each core and create repositories manually.
#Autowired
#Qualifier("solrCurrencyTemplate")
private SolrTemplate solrCurrencyTemplate;
#Autowired
#Qualifier("solrCountryTemplate")
private SolrTemplate solrCountryTemplate;
//...
CurrencyRepository currencyRepo = new SolrRepositoryFactory(this.solrCurrencyTemplate)
.getRepository(CurrencyRepository.class);
CountryRepository countryRepo = new SolrRepositoryFactory(this.solrCountryTemplate)
.getRepository(CountryRepository.class);
Spring Data now supports multiple cores with their respective repositories.
The multicoreSupport flag needs to be true in #EnableSolrRepositories annotation and the corresponding document needs to be told what core they belong to. Like:
#SolrDocument(solrCoreName = "currency")
public class Currency
{
// attributes
}
the other class should be
#SolrDocument(solrCoreName = "country")
public class Country
{
// attributes
}
The respective repositories should know what pojo they are working with.
public interface CurrencyRepository extends SolrCrudRepository<Currency,String>
{
}
and
public interface CountryRepository extends SolrCrudRepository<Country,String>
{
}
and configuration should be
#Configuration
#EnableSolrRepositories(value = "com.package.name",multicoreSupport = true)
public class SolrConfig
{
#Bean
public SolrServer solrServer() throws Exception
{
HttpSolrServerFactoryBean f = new HttpSolrServerFactoryBean();
f.setUrl("http://localhost:8983/solr");
f.afterPropertiesSet();
return f.getSolrServer();
}
#Bean
public SolrTemplate solrTemplate(SolrServer solrServer) throws Exception
{
return new SolrTemplate(solrServer());
}
}
With Spring Data Solr 1.1.0.RC1 multiple cores works as described by Christoph Strobl with #EnableSolrRepositories. It works also with an XML configuration by set multicore-support="true".
<solr:repositories base-package="your.solr.repo.package" repository-impl-postfix="Impl" multicore-support="true"/>
<solr:solr-server id="solrServer" url="${solr.server.base.connection.url}" />
<bean id="solrTemplate" class="org.springframework.data.solr.core.SolrTemplate">
<constructor-arg index="0" ref="solrServer" />
</bean>
<solr:solr-server id="solrServer" timeout="1000" maxConnections="1000" url="${solr.server.1},${solr.server.2}"/>
<bean id="solrServerFactory" class="org.springframework.data.solr.server.support.MulticoreSolrServerFactory">
<constructor-arg ref="solrServer" />
<constructor-arg name="cores">
<list>
<value>${solr.index.customer}</value>
<value>${solr.index.task}</value>
</list>
</constructor-arg>
</bean>
<bean id="solrTemplate" class="org.springframework.data.solr.core.SolrTemplate">
<constructor-arg ref="solrServerFactory" />
</bean>
<solr:repositories base-package="com.deve.pig.solr" multicore-support="true" solr-template-ref="solrTemplate" />

Programmatic access to properties created by property-placeholder

I'm reading properties file using context:property-placeholder. How can I access them programatically (#Value doesn't work - I don't know property titles at the moment of developing)?
The main problem is I can't change applicationContext.xml file because it's setted up by "parent" framework
ps. It's strange but Environment.getProperty returns null
No you can't. PropertyPlaceholderConfigurer is a BeanFactoryPostProcessor, it is only "alive" during bean creation. When it encounters a ${property} notation, it tries to resolve that against its internal properties, but it does not make these properties available to the container.
That said: similar questions have appeared again and again, the proposed solution is usually to subclass PropertyPlaceHolderConfigurer and make the Properties available to the context manually. Or use a PropertiesFactoryBean
We use the following approach to access properties for our applications
<util:properties id="appProperties" location="classpath:app-config.properties" />
<context:property-placeholder properties-ref="appProperties"/>
Then you have the luxury of just autowiring properties into beans using a qualifier.
#Component
public class PropertyAccessBean {
private Properties properties;
#Autowired
#Qualifier("appProperties")
public void setProperties(Properties properties) {
this.properties = properties;
}
public void doSomething() {
String property = properties.getProperty("code.version");
}
}
If you have more complex properties you can still use ignore-resource-not-found and ignore-unresolvable. We use this approach to externalise some of our application settings.
<util:properties id="appProperties" ignore-resource-not-found="true"
location="classpath:build.properties,classpath:application.properties,
file:/data/override.properties"/>
<context:property-placeholder ignore-unresolvable="true" properties-ref="appProperties"/>
#Value
annotation works on new releases of Spring (tested on v3.2.2)
Here is how it is done:
Map your properties file in spring configuration file
<!--Import Info:
xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd-->
<context:property-placeholder location="classpath:/app-config.properties" />
Create app-config.properties inside (root) your source folder
my.property=test
my.property2=test2
Create a controller class
#Controller
public class XRDSBuilder
{
#Value("${my.property}")
private String myProperty;
public String getMyProperty() { return myProperty; }
}
Spring will automatically map the content of my.property to your variable inside the controller
Mapping to a list
Property value:
my.list.property=test,test2,test3
Controller class configuration:
#Value("#{'${my.list.property}'.split(',')}")
private List<String> myListProperty;
Advanced mapping
#Component("PropertySplitter")
public class PropertySplitter {
/**
* Example: one.example.property = KEY1:VALUE1,KEY2:VALUE2
*/
public Map<String, String> map(String property) {
return this.map(property, ",");
}
/**
* Example: one.example.property = KEY1:VALUE1.1,VALUE1.2;KEY2:VALUE2.1,VALUE2.2
*/
public Map<String, List<String>> mapOfList(String property) {
Map<String, String> map = this.map(property, ";");
Map<String, List<String>> mapOfList = new HashMap<>();
for (Entry<String, String> entry : map.entrySet()) {
mapOfList.put(entry.getKey(), this.list(entry.getValue()));
}
return mapOfList;
}
/**
* Example: one.example.property = VALUE1,VALUE2,VALUE3,VALUE4
*/
public List<String> list(String property) {
return this.list(property, ",");
}
/**
* Example: one.example.property = VALUE1.1,VALUE1.2;VALUE2.1,VALUE2.2
*/
public List<List<String>> groupedList(String property) {
List<String> unGroupedList = this.list(property, ";");
List<List<String>> groupedList = new ArrayList<>();
for (String group : unGroupedList) {
groupedList.add(this.list(group));
}
return groupedList;
}
private List<String> list(String property, String splitter) {
return Splitter.on(splitter).omitEmptyStrings().trimResults().splitToList(property);
}
private Map<String, String> map(String property, String splitter) {
return Splitter.on(splitter).omitEmptyStrings().trimResults().withKeyValueSeparator(":").split(property);
}
}
Property value:
my.complex.property=test1:value1,test2:value2
Controller class:
#Value("#{PropertySplitter.map('${my.complex.property}')}")
Map<String, String> myComplexProperty;
Spring follows Inversion Of Control approach, this means that we can simply inject particular property into POJO. But there are some cases, when you would like to access property given by name directly from your code - some might see it as anti-pattern - this is palpably true, but lets concentrate on how to do it.
The PropertiesAccessor below provides access to properties loaded by Property Placeholder and encapsulates container specific stuff. It also caches found properties because call on AbstractBeanFactory#resolveEmbeddedValue(String) is not cheap.
#Named
public class PropertiesAccessor {
private final AbstractBeanFactory beanFactory;
private final Map<String,String> cache = new ConcurrentHashMap<>();
#Inject
protected PropertiesAccessor(AbstractBeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
public String getProperty(String key) {
if(cache.containsKey(key)){
return cache.get(key);
}
String foundProp = null;
try {
foundProp = beanFactory.resolveEmbeddedValue("${" + key.trim() + "}");
cache.put(key,foundProp);
} catch (IllegalArgumentException ex) {
// ok - property was not found
}
return foundProp;
}
}
Found answer at below site:
http://forum.spring.io/forum/spring-projects/container/106180-programmatic-access-to-properties-defined-for-the-propertyplaceholderconfigurer
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" id="propertyConfigurer">
<property name="properties" ref="props" />
</bean>
<bean id="props" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="location" value="file:C:/CONFIG/settings.properties"/>
</bean>
<util:properties id="prop" location="location of prop file" />
This return java.util.Properties object
In JAVA Code
Properties prop = (Properties) context.getBean("prop");
Now you can access ,
prop.getProperty("key");
This works if you need to scan multiple locations for your properties ...
<bean id="yourProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="locations">
<array value-type="org.springframework.core.io.Resource">
<value>classpath:yourProperties.properties</value>
<value>file:../conf/yourProperties.properties</value>
<value>file:conf/yourProperties.properties</value>
<value>file:yourProperties.properties</value>
</array>
</property>
<property name="ignoreResourceNotFound" value="true" />
</bean>
<context:property-placeholder properties-ref="yourProperties" ignore-unresolvable="true"/>
And then in your actual classes ...
#Autowired
Properties yourProperties;
Tested using Spring 5.1.4
Create beans for your properties before putting them in property-placeholder to make the properties easy to access in-code.
Ex:
<bean id="configProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="resources" value="classpath:META-INF/spring/config.properties" />
</bean>
<context:property-placeholder properties-ref="configProperties" ignore-unresolvable="true"/>
Code:
#Autowired
private PropertiesFactoryBean configProperties;
You can also use #Resource(name="configProperties")
Let's asume that you the properties file defined in that "parent" framework
<bean id="applicationProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="location" value="classpath:main.properties" />
</bean>
You can use the #Value annotation in this way:
#Value( value = "#{applicationProperties['my.app.property']}" )
private String myProperty;

Dozer custom converter ID mapping: Object to Long and Long to Object via DozerConverter getParameter

I need help configuring my dozer mapping file.
Mainly I would like to know how to get User user obejct to convert to Long userId.
Hence map: user >> userId
But I have multiple objects such as comment >> commentId or address >> addressId
therefor I'd like to have something more elegant than just writing mapping for each of the fields. All of the object implement Loadable interface.
The bellow code is now functioning thanks to the getParameter() DozerConverter method, but if you know any better way than the converter that I wrote please let me know.
// dozer.xml
<?xml version="1.0" encoding="UTF-8"?>
<mappings xmlns="http://dozer.sourceforge.net"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://dozer.sourceforge.net http://dozer.sourceforge.net/schema/beanmapping.xsd">
<configuration>
<custom-converters>
<converter type="project.shared.domain.dto.dozer.LoadableIdConverter" >
<class-a>project.shared.domain.Loadable</class-a>
<class-b>java.lang.Long</class-b>
</converter>
</custom-converters>
</configuration>
<mapping>
<class-a>project.shared.domain.Suggestion</class-a>
<class-b>project.shared.domain.dto.DTOSuggestion</class-b>
<field custom-converter-param="User">
<a>user</a>
<b>userId</b>
</field>
</mapping>
</mappings>\
// Spring Application context
<bean id="loadableIdConverter" class="project.shared.domain.dto.dozer.LoadableIdConverter">
<property name="userService" ref="userService"/>
<property name="commentService" ref="commentService"/>
<property name="addressService" ref="addressService"/>
</bean>
<bean id="gwtMapper" class="org.dozer.DozerBeanMapper">
<property name="mappingFiles">
<list>
<value>classpath:/dozer.xml</value>
</list>
</property>
<property name="customConverters">
<list>
<ref bean="loadableIdConverter"/>
</list>
</property>
</bean>
//Standard hibernate object
public class Suggestion implements Serializable, Loadable {
private long id = -1;
private Date dateCreated;
private User user; //trying to use dozer to covert this bad boy to Long userId
//...
}
//DTO object
public class DTOSuggestion implements IsSerializable {
private long id = -1;
private Date dateCreated;
private Long userId; //trying to get this ID via the dozer converter
//...
}
//Loadable interface
public interface Loadable extends Serializable {
public long getId();
public void setId(long id);
}
//Dozer converter
public class LoadableIdConverter extends DozerConverter<Loadable, Long> {
private UserService userService; //configured in applicationcontext
private AddressService addressService; //configured in applicationcontext
private CommentService commentService; //configured in applicationcontext
public LoadableIdConverter() {
super(Loadable.class, Long.class);
}
public Long convertTo(Loadable object, Long id) {
return object.getId();
}
public Loadable convertFrom(Long id, Loadable object) {
if (id < 0) return null;
String loadable = getParameter();
if (loadable.equalsIgnoreCase("User"))
return userService.get(User.class, id);
if (loadable.equalsIgnoreCase("Address"))
return addressService.get(Address.class, id);
if (loadable.equalsIgnoreCase("Comment"))
return commentService.get(Comment.class, id);
return null;
}
}
There is one trick you could use to avoid converter parameters. If you fall back to older custom converter approach in Dozer, which is implementing CustomConverter interface, you will get two additional parameters: existingDestinationValue and destinationClass.
convert(Object existingDestinationFieldValue, Object sourceFieldValue, Class<?> destinationClass, Class<?> sourceClass)
By using these values you could introspect your destination field via reflection and know what is the expected concrete implementation of Loadable interface. This works only if you define the field types with concrete types of course. But you already have it in your example, so this should not be a problem. CustomConverter implementation will be more verbose as you need to determine the direction of the mapping manually, but it gives you full control of what is going on during the mapping process.

Resources