package com.protect.shapes;
public class Triangle {
String Type = null;
String type = null;
public String getType() {
return Type;
}
public void setType(String Type) {
this.Type = Type;
}
public String gettype() {
return type;
}
public void settype(String type) {
this.type = type;
}
public void draw() {
System.out.println("Drawing " + type + " Triangle. " + Type);
}
}
Configuration file.
<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">
<!-- Project beans go here -->
<bean id="triangle" class="com.protect.shapes.Triangle">
<property name="Type" value="Equilateral">
</property>
</bean>
I was trying to check how Spring handles case sensitive property names but could not figure it out: for both the type and Type fields I Set I get the same output
"Drawing Equilateral Triangle. null".
Can you please explain this why and how can I instantiate Type field.
Spring can't find a setter for your Type property:
setType won't do because according to the java beans specification that refers to the type property
settype is invalid because it's not recognized as a property setter according to the same specification.
If you want an upper case names property it should begin with (at least) two uppercase letters, e.g.
public void setPType(...) {
...
}
and
<property name="PType" value="..."/>
Check section 8.8 of the specification.
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="" />
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
I would like to be able to choose specific Spring (or Grails) context configuration based on the tenant that user belongs to in runtime. Let's say I use Spring Security and I retrieve tenantId during login.
Imagine now I have a two tenants and they pay different commission. How to inject specific service into a controller without too much plumbing? Here are two different contexts. So, I should inject different ExchangeService based on tenant.
#Configuration
public class FooTenant{
#Bean
public ExchangeService bar() {
return new ZeroCommisionExchangeService ();
}
}
#Configuration
public class BarTenant{
#Bean
public ExchangeService bar() {
return new StandardCommisionExchangeService ();
}
}
Edit:
I am aware I can obtain reference to Spring context and ask for service "manually", but I am looking for a more generic solution where this problematic is solved by IoC framework.
A couple of years ago we needed somthing like this but only for DataSources and ViewResolvers. We developed a solution using spring' TargetSource solution. (Initially we used a HotswappableTargetSource but that wasn't adequate for our use-case.
The code we developed is availabe here in the multi-tenant directory.
It is fully configurable and flexible.
Basically what you do is you configura a ContextSwappableTargetSource and tell it what type of interface/class it needs to return.
<bean id="yourTentantBasedServiceId" class="biz.deinum.multitenant.aop.target.ContextSwappableTargetSource">
<constructor-arg value="ExchangeService" />
</bean>
The default is to lookup beans in the ApplicationContext based on the tenantId (see the BeanFactoryTargetRegistry for this). However you can specify one or more of those (we used a JndiLookupTargetRegistry to dynamically lookup datasource, which allowed use to add tenants on the fly without restarting the application).
If you explicitly configure a BeanFactoryTargetRegistry you can add a prefix and suffix.
<bean id="exchangeService" class="biz.deinum.multitenant.aop.target.ContextSwappableTargetSource">
<constructor-arg value="ExchangeService" />
<property name="targetRegistry>
<bean class="biz.deinum.multitenant.aop.target.registry.impl.BeanFactoryTargetRegistry">
<property suffix="ExchangeService"/>
</bean>
</property>
</bean>
Now for foo it would lookup a bean named fooExchangeService and for bar barExchangeService.
The tenantId is stored in a ThreadLocal which is wrapped inside the ContextHolder. You need to find a way to fill and clear this thread local (in general a servlet Filter does that trick.
In your code you can now simply use the interface ExchangeService and at runtime based on the tenantId the correct implemenation will be looked up.
Also see http://mdeinum.wordpress.com/2007/01/05/one-application-per-client-database/
Assuming you have different services already defined, you can get their bean from the context and use it. In my example, all the services have implementation of serviceMethod and based on some criteria pick your proper service. The only thing I am not sure is how Multitenancy might impact this.
import org.springframework.context.ApplicationContext
class ServiceManagerController {
def serviceManager
def index() {
ApplicationContext ctx = grails.util.Holders.grailsApplication.mainContext
serviceManager = ctx.getBean(params.serviceName); //firstService or secondService
render serviceManager.serviceMethod()
}
}
FirstService
class FirstService {
def serviceMethod() {
return "first"
}
}
SecondService:
class SecondService {
def serviceMethod() {
return "second"
}
}
While it is possible to swap beans instantiated in a spring context at runtime (HotswappableTargetSource), it is not meant for use cases such as yours.
Remember there is one Spring Context for your application, all threads use the same instances (in most cases), this implies when you swap out a bean implementation, you are affectively doing this for all your application's users. To prevent this, you run into issues of ensuring Thread Safety, employing Thread Locals, as listed in another answer.
While it is possible to continue this approach and arrive at an implementation that gets the job done, it would definitely be a very contrived way of solving this problem.
You should take a step back and look at your problem in a more wholesome, system wide design point of view. Bust out your patterns books and look at how this can be resolved, regardless of whether you use Spring or an other framework. Service Locator, Factory bean etc described in some of the answers above is a step in the correct direction.
Your Use Case is pretty common for multi-tenant applications. You need to narrow down things that are likely to change based on a tenantId versus things that are constant across.
For instance as mentioned in the question, each Tenant might have a different commission amount or even different algorithm for commission calculation. A simple solution to this would be to implement a CommissionCalculationService which accepts a tenantId, and any other domain object based on which commission is to be calculated, I would imagine this would be something like Order or Sale, whatever makes sense in your application.
You now need a CommissionServiceFactory or a ServiceLocator which will contain tenant specific implementations of the CommissionCalculationService. The Service Locator is instantiated when the Spring context loads, and is injected with implementation classes also at application startup.
When you want to calculate commission for a tenant, you basically obtain the tenantId from the user's login, pass the tenant id to your service locator, based on the tenantId passed, the service locator returns the appropriate instance of a Service Implementation. In your calling class, use this instance to calculate the commission for the tenant.
Another pattern to consider is the Strategy Pattern, or even Template Pattern.
Bottom line, even if you want tenant specific logic implemented cleanly, don't thing about changing the beans loaded in the context. Have classes in your context that can handle all your tenant specific logic. Rely on design patterns to use the correct bean from the context based on the tenant id.
I apologize if the answer was a little verbose, I felt it was needed to explain why I think updating beans in a loaded Spring Context is not the appropriate solution.
I use the following code:
public class ConfigurableProxyFactoryBean implements FactoryBean<Object>, BeanNameAware {
#Autowired
private ApplicationContextProvider applicationContextProvider;
private Class<?> proxyType;
private String beanName;
private Object object;
private Object fallbackObject;
private Object monitor = new Object();
private ConfigurableProxy proxy;
public ConfigurableProxyFactoryBean(Class<?> proxyType) {
this.proxyType = proxyType;
}
public Object getFallbackObject() {
return fallbackObject;
}
public void setFallbackObject(Object fallbackObject) {
synchronized (monitor) {
this.fallbackObject = fallbackObject;
if (proxy != null) {
proxy.setFallbackObject(fallbackObject);
}
}
}
#Override
public void setBeanName(String name) {
beanName = name;
}
#Override
public Object getObject() throws Exception {
synchronized (monitor) {
if (object == null) {
#SuppressWarnings("unchecked")
Class<Object> type = (Class<Object>)proxyType;
proxy = new ConfigurableProxy(applicationContextProvider, beanName);
proxy.setFallbackObject(fallbackObject);
object = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
new Class<?>[] { type }, proxy);
}
return object;
}
}
#Override
public Class<?> getObjectType() {
return proxyType;
}
#Override
public boolean isSingleton() {
return true;
}
}
class ConfigurableProxy implements InvocationHandler {
public ConfigurableProxy(ApplicationContextProvider appContextProvider, String beanName) {
this.appContextProvider = appContextProvider;
this.beanName = beanName;
}
private ApplicationContextProvider appContextProvider;
private String beanName;
private Object fallbackObject;
#Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
ApplicationContext appContext = appContextProvider.getApplicationContext();
String name = "$&&#" + beanName;
Object bean = appContext.containsBean(name) ? appContext.getBean(name) : fallbackObject;
return method.invoke(bean, args);
}
public void setFallbackObject(Object fallbackObject) {
this.fallbackObject = fallbackObject;
}
}
ApplicationContextProvider has implementation, that chooses ApplicationContext according to current tennant.
In XML configuration it is used like this:
<bean class="my.package.infrastructure.ConfigurableProxyFactoryBean" name="beanName">
<constructor-arg>
<value type="java.lang.Class">my.package.model.ServiceInterface</value>
</constructor-arg>
<property name="fallbackObject">
<bean class="my.package.service.DefaultServiceImplementation"/>
</property>
</bean>
And in tennant configuration that way:
<bean class="my.package.service.ServiceImplementationA" name="$&&#beanName"/>
To inject this service somewhere you just write:
public class MyController {
#Autowired
private ServiceInterface service;
}
Also you are to implement ApplicationContextProvider, I won't share mine. It is not very hard to implement. For example, your implementation can just store context in ThreadLocal. And you create your own ServletContextListener, which for every query gets the current tennant and stores it into your ApplicationContextProvider implementation.
The new tenant scope and a servicelocator can helps
Tenant scope will guarantee than service is created one time for a tenant
Sample code:
<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
<property name="scopes">
<map>
<entry key="tenant" value="foo.TenantScope"/>
</map>
</property>
</bean>
<bean id="service" class="foo.Service" factory-bean="tenantServiceLocator" factory-method="createInstance" scope="tenant"/>
<bean id="fooService" class="FooService">
<bean id="barService" class="BarService">
<bean id="tenantServiceLocator" class="foo.TenantServiceLocator">
<property name="services">
<map>
<entry key="foo" value-ref="fooService"/>
<entry key="bar" value-ref="barService"/>
</map>
</property>
</bean>
TenantServiceLocator should know the user tenantId
public class TenantServiceLocator {
private Map<String, Service> services;
public String getTenantId() {
return "foo"; // get it from user in session
}
public Map<String, Service> getServices() {
return services;
}
public void setServices(Map<String, Service> services) {
this.services = services;
}
public Service createInstance(){
return services.get(tenantId);
}
}
public class FooController{
#Autowired
private Service service;
}
A sample TenantScope implementation
public class TenantScope implements Scope {
private static Map<String, Map<String, Object>> scopeMap = new HashMap<String, Map<String, Object>>();
#Override
public Object get(String name, ObjectFactory<?> objectFactory) {
Map<String, Object> scope = getTenantScope(getTenantId());
Object object = scope.get(name);
if(object == null){
object = objectFactory.getObject();
scope.put(name, object);
}
return object;
}
private Map<String, Object> getTenantScope(String tenantId) {
if (!scopeMap.containsKey(tenantId)) {
scopeMap.put(tenantId, new HashMap<String, Object>());
}
return scopeMap.get(tenantId);
}
private String getTenantId() {
return "foo"; // load you tenantId
}
#Override
public Object remove(String name) {
Map<String, Object> scope = getTenantScope(getTenantId());
return scope.remove(name);
}
#Override
public void registerDestructionCallback(String name, Runnable callback) {
}
#Override
public Object resolveContextualObject(String key) {
return null;
}
#Override
public String getConversationId() {
return null;
}
}
Transforming my comment in an answer, one possible solution is to create a spring factory bean, that receive all he needs to decide which service needs to be returned when creating the instance.
Translating to Grails:
public interface ChoosableServiceIntf {
String getName();
}
class NormalService implements ChoosableServiceIntf {
public String getName() {
return getClass().name;
}
}
class ExtendedService implements ChoosableServiceIntf {
public String getName() {
return getClass().name
}
}
class ChoosableServiceFactory {
static ChoosableServiceIntf getInstance(String decisionParam) {
if(decisionParam == 'X') {
return applicationContext.getBean('extendedService')
}
return applicationContext.getBean('normalService')
}
static ApplicationContext getApplicationContext() {
return Holders.grailsApplication.mainContext
}
}
Here we have two services and ChoosableServiceFactory is responsible to know witch is the correct one.
Then you will need to use the method ApplicationContext#getBean(String, Object[]) to return the correct instance and will also make the factory prototyped scope because of the runtime params.
A controller to test it:
class MyController {
def grailsApplication
def index() {
ChoosableServiceIntf service = grailsApplication.mainContext.getBean('choosableServiceFactory', ["X"] as Object[])
ChoosableServiceIntf serviceNormal = grailsApplication.mainContext.getBean('choosableServiceFactory', ["N"] as Object[])
render text: "#1 - ${service.class.name} , #2 - ${serviceNormal.class.name}"
}
}
This will print #1 - dummy.ExtendedService , #2 - dummy.NormalService
The declaration of the beans will be:
choosableServiceFactory(ChoosableServiceFactory) { bean ->
bean.scope = 'prototype'
bean.factoryMethod = 'getInstance'
}
normalService(NormalService)
extendedService(ExtendedService)
I am new for spring security. I've seen many posts on how to inject values via annotation from external property file. I've tried many ways, but I always end up with java.lang.IllegalArgumentException: Could not resolve placeholder 'val.id' exception.
Can you provide me some tips how to handle this exception please?
My java class is the following one:
#Controller
public class Employee {
#Value("${val.id}")
public String valId;
public String getValId() {
return valId;
}
public void setValId(String valId) {
this.valId = valId;
}
My property file is called val.properties which is located under WEB-INF, and its content is
val.id=xyz
I put the following in my main context bean.
<context:property-placeholder location="/WEB-INF/*.properties" />
<bean id="valProp" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
p:location="/WEB-INF/val.properties"/>
A continuous question:
The injecting values from properties file to annotated beans works fine as I accepted the answer above. However, I cannot able to inject it to #PreAuthorize(...) annotation by following the same procedure.
Assume I want to secure a method called 'update'. This method is allowed if and only if valId is equal to empId. values of valId and empId are initialized in the val.properties file.
my java bean is:
public class Employee {
public String valId;
public String empId;
public String getValId() {
return valId;
}
public void setValId(String valId) {
this.valId = valId;
}
public String getEmpId() {
return empId;
}
public void setEmpId(String empId) {
this.empId = empId;
}
}
my property file contains:
val.id=nn
emp.id=nn
I have the place holder configuration in my main context file:
<context:property-placeholder location="/WEB-INF/*.properties" />
<bean id="valProp" class="org.springframework.beans.factory.config.PropertiesFactoryBean"
p:location="/WEB-INF/val.properties"/>
My PreAuthorize annotation (method security) is:
#PreAuthorize("(#{valProp['val.id']} == #{valProp['emp.id']})")
public boolean update(){
//if accessable
return true;
}
But the expression #{valProp['val.id']} == #{valProp['emp.id']} is not evaluated.
Did I do any mistake to inject values? It was worked when I annotate member variables, but it doesn't work here. Any idea please? Thanks in advance.
try to consider the following
1). change your annotation to:
#Value("#{valProp['val.id']}")
2). Replace PropertyPlaceholderConfigurer by PropertiesFactoryBean.
Hope this will resolve the exception.
The reason why the exception is thrown is, because the property placeholder by default throws an exception when a values cannot be resolved.
Furthermore you have two property placeholders, via which probably not all values can be resolved.
You can change this behaviour via setting the ignore-unresolvable property:
<context:property-placeholder location="/WEB-INF/*.properties" ignore-unresolvable="true" />
<bean id="valProp" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
p:location="/WEB-INF/val.properties" p:ignoreUnresolvablePlaceholders="true" />
Note however that b< turning off this feature typos in a property file will not be detected.