I have problem making Spring Cache working in OSGi Environment. Maybe you can show me what i am missing.
I have configured Spring Cache successfully to work during tests like
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"classpath:spring/spring-test.xml"})
public class CacheDictTest {
#Autowired
Dictionary dictionary;
#Test
public void getDict5Times() {
for (int i = 0; i < 5; i++) {
System.out.println(dictionary.getSourceDomains());
}
Assert.assertTrue(true);
}
}
The select is executed once and then i have 5 nice prints.
However I cannot make it work in a bundle
The Cacheable annotation seems to be ignored. Queries are performed everytime i call dictionary.getSourceDomains().
I use ServiceMix 5.3.0 as a container.
My configuration:
<cache:annotation-driven cache-manager="cacheManager"/>
<bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
<property name="caches">
<set>
<bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="dictionary"/>
</set>
</property>
</bean>
dictionary:
public class DictionaryImpl implements Dictionary {
private DictionaryDao repository;
public DictionaryDao getRepository() {
return repository;
}
public void setRepository(DictionaryDao repository) {
this.repository = repository;
}
#Override
public List<String> getSourceDomains() {
List<DictEntry> entries = repository.getDictionary(DictTypeEnum.SOURCE_DOMAIN);
List<String> domains = new ArrayList<>();
for(DictEntry entry : entries) {
domains.add(entry.getKey());
}
return domains;
}
}
and dao
public class DictionaryDaoImpl extends BaseDaoImpl implements DictionaryDao {
private static final Logger LOG = LoggerFactory.getLogger(DictionaryDaoImpl.class);
#Override
#Cacheable(value="dictionary", key="#type")
public List<DictEntry> getDictionary(DictTypeEnum type) {
LOG.info("Loading {}", type);
Query q = getSession().createQuery("from DictEntry where type=:type");
q.setParameter("type", new DictType(type.getTypeId()));
List results = q.list();
LOG.debug("Results {}", results);
return results;
}
}
What i tried
Moving #Cacheable annotation to DictionaryDao (interface), to DictionaryImpl or Dictionary (interface) - no effect.
Use different cache implementantion (ehcache instead of JDK ConcurrentMap-based Cache) - no effect
The problem was missing import of Cacheable annotation package in osgi manifest.
org.springframework.cache.annotation
Servicemix did not show any error for missing class, just let the service work ignoring Cacheable annotation.
Related
I have a project which uses an old spring.jar (1.2.6),from this project, I am expected to call a newer version (spring version 5.0.7) spring boot project's method. Below is the way I am creating my bean in old version project.
I am getting NullPointer exception while creating the Autowired bean.
Create bean from XML:spring
test-context.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "qvc-spring-beans.dtd">
<beans>
<bean name="testPci" class="com.test.client.TestPci">
</bean>
</beans>
sampleParent-context.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans SYSTEM "spring-beans.dtd">
<beans>
<import resource="classpath:/com/test/test-context.xml" />
<bean id="classA" class="com.test.A" >
<property name="testPci">
<ref bean="testPci"/>
</property>
</bean>
</beans>
Java code old spring project:
package com.test;
public class A{
private TestPci testPci;
private ApplicationContext ctx;
public TestPci getTestService() {
if (!StringUtils.isValid(ctx)) {
ctx = new ClassPathXmlApplicationContext("./com/test/test-context.xml");
}
if (!StringUtils.isValid(this.testPci)) {
if (StringUtils.isValid(ctx)) {
testPci = (TestPci) ctx.getBean("testPci");
TestPci testPci = (TestPci) ctx
.getBean("testPci");
this.setSecureTestService(testPci);
}
}
return this.getSecureTestService();
}
public TestPci getSecureTestService() {
return testPci;
}
public void setSecureTestService(TestPci testPci) {
this.testPci = testPci;
}
public void methodA(){
//Calling newer code form old spring code:
testPci.testing("1", "2", "3");
}
}
Calling "TestPci" class as above, but when trying to call using the above, it actually calls the "TestPci"."testing" method. But the object autowired as "testWebClientService" is returning as null. I would like to get the object created instead it returns null.
New spring version class:
#Service
#EnableConfigurationProperties(TestWebClientProperties.class)
#Configurable
public class TestPci{
#Autowired
private TestWebClientService testWebClientService;
public Map<String, String> testing(String a, String b, String c) throws Exception {
Map<String, String> map = testWebClientService.find(a, b, c);
System.out.println("**=="+map.get(0));
return map;
}
}
Adding junit which is used to call the TestPci class from newer version of spring:
#RunWith(SpringJUnit4ClassRunner.class)
#EnableConfigurationProperties(TestWebClientProperties.class)
#SpringBootTest(classes = { TestWebClientService.class, TestPci.class }, webEnvironment = WebEnvironment.NONE)
public class TestJunit {
#MockBean(name="restTemplate")
public RestTemplate restTemplate;
#Autowired
private TestPci testPci;
#Test
public void ff() throws Exception {
testPci.testing("1","1","1");
}
}
In configurations I have a flag isFlagEnabled.
So I have to read the flag from spring config and based on that I want to execute specific class A or B . Meaning I want to load A class only when isFlagEnabled is true and similarly load class B only when isFlagEnabled is false.
I have written the below code but i am stuck when ingesting .
public interface MediatorInt {
public void init();
}
class A implements MediatorInt {
init() { It does some task }
}
class B implements MediatorInt {
init(){ It does some task }
}
public class MasterNewGenImpl {
#Autowired
#Qualifier("config")
private Configuration config;
#Autowired
MediatorInt mediatorInt;
private final Logger logger = Logger.getLogger(getClass());
public void startService() {
mediatorInt.init();
}
}
context.xml file
<context:component-scan base-package="com.ca"/>
<bean id="config" class="com.ca.configuration.ConfigImplementation"/>
<bean id="masterSlave" class="com.ca.masterslave.A"/>
<bean id="systemState" class="com.ca.masterslave.B"/>
<bean id="masterSlaveNewGen" class="com.ca.masterslave.MasterNewGenImpl">
<property name = "mediatorOrMasteSlave" value="#{config.getMediatorMode() == 'true' ? 'systemState' : 'masterSlave'}" />
</bean>
So now i am not getting how to inject specific object based on the config flag . I want to make it through Lazy-init so that other object will not get loaded when its not required .
I greatly appreciate the suggestions.
If you are okay with spring scanning both the implementations, then you can select the needed one using #Qualifier. If you want spring not to scan some class based on a property, You can use #Conditional
class SomeCondition implements Condition {
#Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
String isFlagEnabled = context.getEnvironment().getProperty("isFlagEnabled");
return isFlagEnabled.equals("true"));
}
}
#Configuration
#Conditional(value = SomeCondition.class)
class A implements MediatorInt {
init() { It does some task }
}
In the above config, class A is scanned only if matches() in SomeCondition class returns true, where you can define the condition.
You can use
#Autowired
#Qualifier( "systemState" )
MediatorInt systemSateMeidator;
#Autowired
#Qualifier( "masterSlave" )
MediatorInt masterSateMeidator;
With #Qualifier you are instructing spring on how to fulfill your component request.
The problem started when in Spring 4.2.5 I attempted to inject an interface having more than one implementations, below is the test class :
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = VenueConfig.class)
public class BandEventTest {
#Autowired
#Qualifier("rush")
Band rush;
#Autowired
Venue venue;
#Test
public void assertBandNotNull() {
Assert.assertNotNull("Who will perform if there's no band !!!", rush);
}
#Test
public void assertVenueNotNull() {
Assert.assertNotNull("Without venue, there would be no event !!!", venue);
}
#Test
public void triggerEvent() {
venue.hostPerformance();
}
}
I have the interface Band which is implemented by two beans viz. Rush and VanHalen :
#Component("rush")
public class Rush implements Band {
/*
* (non-Javadoc)
*
* #see com.book.springinaction.chap2.autowiring.Band#play()
*/
public void play() {
// TODO Auto-generated method stub
System.out.println("Rush playing 'Limelight'");
}
}
In spite of using the Qualifier annotation(please ignore the lower/upper case current Qualifier name and the Rush bean name, I have tried all the combinations in vain). My suspicion was confirmed after I read this thread about the QualifierAnnotationAutowireCandidateResolver not being set in App. context since Spring 4.0
I wish to fit the below (mentioned even in the Spring documentation) :
<bean id="customAutowireConfigurer" class="org.springframework.beans.factory.annotation.CustomAutowireConfigurer">
<property name="customQualifierTypes">
<set>
<value>org.springframework.beans.factory.annotation.Qualifier</value>
</set>
</property>
</bean>
in my below Configuration class WITHOUT XML i.e JUST ANNOTATIONS
#Configuration
#ComponentScan
public class VenueConfig {
/*Does using #Bean for CustomAutowireConfigurer make sense ?*/
}
I don't use Spring annotations, but your pointer to the other thread was so helpful that I did a bit of research. Based on the Spring docs, I believe you can have the same effect with:
#Configuration
#ComponentScan
public class VenueConfig {
#Bean
public CustomAutowireConfigurer qualifierAutowireConfigurer() {
CustomAutowireConfigurer customAutowireConfig = new CustomAutowireConfigurer();
customAutowireConfig.setCustomQualifierTypes( Collections.singleton( Qualifier.class ));
return customAutowireConfig;
}
}
I'm trying to get to grips with EasyMock in order to run some server side integration tests on a spring-ws web service. I have a DAO which I want to mock for my integration testing, I've managed to autowire it successfully, but I can't figure out how to set the expectations post autowire.
I have the following in my spring context xml:
<bean id="accountServiceDao" class="org.easymock.EasyMock" factory-method="createMock">
<constructor-arg value="com.xxx.account.dao.AccountServiceDao" />
</bean>
<bean id="notMockedDao" class="com.xxx.account.dao.AccountServiceDaoImpl"/>
<context:component-scan base-package="com.xxx.account" />
<context:property-placeholder location="classpath:accountDetailService_test.properties" />
<sws:annotation-driven />
<jdbc:embedded-database id="dataSource" type="HSQL">
<jdbc:script location="classpath:sql/db_schema.sql" />
<jdbc:script location="classpath:sql/test_data.sql" />
</jdbc:embedded-database>
My dummy test is as follows:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "file:src/main/webapp/WEB-INF/applicationContext_test.xml" })
public class AccountDetailServiceMockIntergrationTest {
#Autowired
private ApplicationContext applicationContext;
private MockWebServiceClient mockClient;
#Before
public void createClient() {
mockClient = MockWebServiceClient.createClient(applicationContext);
/* Set the expectations for the autowired mock dao here */
}
#Test
public void customerEndpoint() throws Exception {
Source requestPayload = new StringSource(TestData.requestXML);
Source responsePayload = new StringSource(TestData.responseXML);
mockClient.sendRequest(withPayload(requestPayload)).andExpect(
payload(responsePayload));
}
}
The endpoint which is hit is below:
#Autowired
private AccountService accountService;
#PayloadRoot(localPart = "AccountSearchRequest", namespace = TARGET_NAMESPACE)
public #ResponsePayload
AccountSearchResponse getAccountDetails(
#RequestPayload AccountSearchRequest request) {
logger.info("Received request | debtornum - " + request.getDebtornum());
AccountSearchResponse accountSearchResponse = objectFactory.createAccountSearchResponse();
AccountDetailsType accountDetails = accountService.getAccountDetails(request.getDebtornum());
accountSearchResponse.setAccountDetails(accountDetails);
logger.info("Returned response | status - " + accountSearchResponse.getAccountDetails().getDebtorStatus().value());
return accountSearchResponse;
}
And here's the service class which contains the DAO which is being mocked
#Service
public class AccountServiceImpl implements AccountService {
//Autowired on a setter
private AccountServiceDao accountServiceDao;
Logger logger = LoggerFactory.getLogger(AccountServiceImpl.class);
#Override
public AccountDetailsType getAccountDetails(BigInteger accountNumber) {
........................
Via debug I can see that the mock DAO is getting injected correctly, but I don't know how to set the behavior on the mock object.
For my unit tests I was able to do the following:
accountDao = EasyMock.createMock(AccountServiceDao.class);
EasyMock.expect(accountDao.checkAccountExists(new BigInteger("12345678"))).andReturn(new Account(new BigInteger("12345678"),"Y",1,0,0,0,"ROI","ROI","DO","10012054082","POST","DD","John Doe","a#a.com","123456"));
EasyMock.replay(accountDao);
testSvc.setAccountServiceDao(accountDao);
I'm not sure how to do the same configuration when the mock is autowired via spring xml config. I'm probably missing something obvious or misunderstanding EasyMock, but any help or guidance would be appreciated.
Thanks
As per Dan's comment above, I've set my expectations as follows:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "file:src/main/webapp/WEB-INF/applicationContext_test.xml" })
public class AccountDetailServiceMockIntergrationTest {
#Autowired
private ApplicationContext applicationContext;
private MockWebServiceClient mockClient;
#Before
public void createClient() {
mockClient = MockWebServiceClient.createClient(applicationContext);
//get the mocked bean from the applicationContext
AccountServiceDao svcDao = (AccountServiceDao)applicationContext.getBean("accountServiceDao");
//reset just in case
EasyMock.reset();
//set expectations on the mock
EasyMock.expect(svcDao.checkAccountExists(new BigInteger("12345678"))).andReturn(new Account(new BigInteger("12345678"),"Y",1,0,0,0,"ROI","ROI","DO","10012054082","POST","DD","John Doe","a#a.com","123456"));
EasyMock.replay(svcDao);
}
#Test
public void customerEndpoint() throws Exception {
Source requestPayload = new StringSource(TestData.requestXML);
Source responsePayload = new StringSource(TestData.responseXML);
mockClient.sendRequest(withPayload(requestPayload)).andExpect(
payload(responsePayload));
}
}
Works fine.
Hello I want to make an intercept url pattern and access dynamically by using sql query in spring security.
Generally we use this type of notation in XML and I want to take these values (/add-role and ROLE_ADMIN) from database.
<intercept-url pattern="/add-role*" access="ROLE_ADMIN" />
Is it possible to do this dynamically?
Disclaimer
As the Spring Security FAQ mentions, the first thing you should do is ask should I really do this? Security is complicated and the configuration should be tested extensively. Allowing the configuration to change dynamically only further complicates things making the application that much more vulnerable. If you really want to do this, the FAQ outlines a basic method to accomplish this. I have expanded upon the FAQ's answer below.
Implement Custom FilterInvocationSecurityMetadataSource
To obtain the security URL mappings dynamically you can implement your own FilterInvocationSecurityMetadataSource. An example implementation is given below.
NOTE: Keep in mind that getAttributes will be invoked for every request that Spring Security intercepts so you will most likely want some sort of caching.
public class JdbcFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
FilterInvocation fi = (FilterInvocation) object;
String url = fi.getRequestUrl();
HttpServletRequest request = fi.getHttpRequest();
// Instead of hard coding the roles lookup the roles from the database using the url and/or HttpServletRequest
// Do not forget to add caching of the lookup
String[] roles = new String[] { "ROLE_ADMIN", "ROLE_USER" };
return SecurityConfig.createList(roles);
}
public Collection<ConfigAttribute> getAllConfigAttributes() {
return null;
}
public boolean supports(Class<?> clazz) {
return FilterInvocation.class.isAssignableFrom(clazz);
}
}
Create a BeanPostProcessor
You cannot use the namespace to wire it up, so taking another tip from the FAQ you can use a BeanPostProcessor which might look like:
public class FilterInvocationSecurityMetadataSourcePostProcessor implements BeanPostProcessor, InitializingBean {
private FilterInvocationSecurityMetadataSource securityMetadataSource;
public Object postProcessAfterInitialization(Object bean, String name) {
if (bean instanceof FilterSecurityInterceptor) {
((FilterSecurityInterceptor)bean).setSecurityMetadataSource(securityMetadataSource);
}
return bean;
}
public Object postProcessBeforeInitialization(Object bean, String name) {
return bean;
}
public void setSecurityMetadataSource(FilterInvocationSecurityMetadataSource securityMetadataSource) {
this.securityMetadataSource = securityMetadataSource;
}
public void afterPropertiesSet() throws Exception {
Assert.notNull(securityMetadataSource,"securityMetadataSource cannot be null");
}
}
XML Configuration
Then, assuming both of the above beans are in the package sample, you would add the following configuration
<bean class="sample.FilterInvocationSecurityMetadataSourcePostProcessor">
<property name="securityMetadataSource">
<bean class="sample.JdbcFilterInvocationSecurityMetadataSource"/>
</property>
</bean>
Possible Problems
If you end up getting a ClassCastException, you are likely running into SEC-1957 which was fixed in Spring Security 3.1.1+ Try updating to the latest version to resolve this.
You cant really get those values from the databse, but you can write a custom code called DecisionManager that evaluates if the resource is allowed to execute. With that code you can even read data from the database.
<bean id="MyDecisionManagerBean" class="org.springframework.security.vote.UnanimousBased">
<property name="decisionVoters">
<list>
<!-- <bean class="org.springframework.security.vote.RoleVoter"/> -->
<bean class="org.springframework.security.vote.RoleHierarchyVoter" >
<constructor-arg>
<bean class="org.springframework.security.userdetails.hierarchicalroles.RoleHierarchyImpl" factory-bean="roleHierarchyImplFactory" factory-method="createRoleHierarchyImpl"/>
</constructor-arg>
</bean>
<bean class="com.mycompany.RoleDenyVoter"/>
<bean class="com.mycompany.RoleAllowVoter"/>
</list>
</property>
</bean>
Your class will be like this :
public class RoleDenyVoter implements AccessDecisionVoter {
public int vote(Authentication authentication, Object object, ConfigAttributeDefinition config) {
//read from the DB and decide if access is granted
the process is documented here :
http://static.springsource.org/spring-security/site/docs/3.0.x/reference/authz-arch.html#authz-voting-based
I have created this entry for update purpose
Implement Custom FilterInvocationSecurityMetadataSource
This class only obtains the URL in every request and lookup their permissions from the database or third party applications
public class CommonFilterSecurityMetaDataSource implements FilterInvocationSecurityMetadataSource {
private final Map<String, UrlRequestModel> permissions;
#Autowired
private UrlRequestDao urlRequestDao;
public CommonFilterSecurityMetaDataSource() {
permissions = new Hashtable<>();
}
public List<ConfigAttribute> getAttributes(Object object) {
final FilterInvocation fi = (FilterInvocation) object;
final String url = fi.getRequestUrl();
final String httpMethod = fi.getRequest().getMethod();
final String key = String.format("%s %s", httpMethod, url);
final UrlRequestModel urlRequestModel;
List<ConfigAttribute> attributes = null;
// Lookup your database (or other source) using this information and populate the
// list of attributes
if(permissions.containsKey(key)) {
urlRequestModel= permissions.get(key);
} else {
urlRequestModel= catRequestDao.findByUrl(url);
if(catRequestMapModel != null) {
permissions.put(key, urlRequestModel);
}
}
if (catRequestMapModel != null) {
List<RoleModel> roles = ulrRequestModel.getRoleList();
if(!roles.isEmpty()) {
attributes = new ArrayList<>(roles.size());
for (RoleModel role : roles) {
attributes.add(new SecurityConfig(role.getDescription()));
}
}
}
return attributes;
}
public Collection<ConfigAttribute> getAllConfigAttributes() {
return null;
}
public boolean supports(Class<?> clazz) {
return FilterInvocation.class.isAssignableFrom(clazz);
}
}
Java configuration
For java configuration only add this to your class wich extends from WebSecurityConfigurerAdapter
#Override
protected void configure(HttpSecurity http) throws Exception {
http.headers().frameOptions().disable();
http.authorizeRequests().
antMatchers( "/javax.faces.resource/**").permitAll().
and()
.exceptionHandling().accessDeniedPage("/accessDenied.jsf").
and().formLogin().
loginPage("/login.jsf").
loginProcessingUrl("/loginAction").
usernameParameter("app_username").
passwordParameter("app_password").
defaultSuccessUrl("/secure/index.jsf").
and().logout().
logoutUrl("/appLogout").
logoutSuccessUrl("/login.jsf").logoutRequestMatcher(new AntPathRequestMatcher("/appLogout")).
and().addFilterAfter(filterSecurityInterceptor(), FilterSecurityInterceptor.class);
http.csrf().disable();
}
#Bean
public FilterSecurityInterceptor filterSecurityInterceptor() throws Exception {
FilterSecurityInterceptor filterSecurityInterceptor = new FilterSecurityInterceptor();
filterSecurityInterceptor.setSecurityMetadataSource(securityMetadataSource());
filterSecurityInterceptor.setAuthenticationManager(authenticationManager());
filterSecurityInterceptor.setAccessDecisionManager(accessDecisionManager());
filterSecurityInterceptor.setPublishAuthorizationSuccess(true);
return filterSecurityInterceptor;
}
#Bean
public AccessDecisionManager accessDecisionManager() {
AuthenticatedVoter authenticatedVoter = new AuthenticatedVoter();
RoleVoter roleVoter = new RoleVoter();
List<AccessDecisionVoter<? extends Object>> voters = new ArrayList<>();
voters.add(authenticatedVoter);
voters.add(roleVoter);
return new AffirmativeBased(voters);
}
#Bean
public FilterInvocationSecurityMetadataSource securityMetadataSource() {
return new CommonFilterSecurityMetaDataSource();
}
I tested it using Spring security 5.0.8