Shiro Authorization Permission check using Annotation not working - spring

Platform: Shiro 1.1.0, Spring 3.0.5
I'm trying to secure the MVC Controller methods using Shiro annotation. However something is wrong with annotations. Regular calls are just working OK. There is nothing specific in Shiro debug also.
My shiro configuration:
<!-- Security Manager -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="sessionMode" value="native" />
<property name="realm" ref="jdbcRealm" />
<property name="cacheManager" ref="cacheManager"/>
</bean>
<!-- Caching -->
<bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
<property name="cacheManager" ref="ehCacheManager" />
</bean>
<bean id="ehCacheManager"
class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" />
<bean id="sessionDAO"
class="org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO" />
<bean id="sessionManager"
class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
<property name="sessionDAO" ref="sessionDAO" />
</bean>
<!-- JDBC Realm Settings -->
<bean id="jdbcRealm" class="org.apache.shiro.realm.jdbc.JdbcRealm">
<property name="name" value="jdbcRealm" />
<property name="dataSource" ref="dataSource" />
<property name="authenticationQuery"
value="SELECT password FROM system_user_accounts WHERE username=? and status=1" />
<property name="userRolesQuery"
value="SELECT role_name FROM system_roles r, system_user_accounts u, system_user_roles ur WHERE u.user_id=ur.user_id AND r.role_id=ur.role_id AND u.username=?" />
<property name="permissionsQuery"
value="SELECT permission_name FROM system_roles r, system_permissions p, system_role_permission rp WHERE r.role_id=rp.role_id AND p.permission_id=rp.permission_id AND r.role_name=?" />
<property name="permissionsLookupEnabled" value="true"></property>
</bean>
<!-- Spring Integration -->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />
<!-- Enable Shiro Annotations for Spring-configured beans. Only run after
the lifecycleBeanProcessor has run: -->
<bean id="annotationProxy"
class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
depends-on="lifecycleBeanPostProcessor" />
<bean
class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager" />
</bean>
<!-- Secure Spring remoting: Ensure any Spring Remoting method invocations
can be associated with a Subject for security checks. -->
<bean id="secureRemoteInvocationExecutor"
class="org.apache.shiro.spring.remoting.SecureRemoteInvocationExecutor">
<property name="securityManager" ref="securityManager" />
</bean>
<!-- Shiro filter -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager" />
<property name="loginUrl" value="/login" />
<property name="successUrl" value="/dashboard" />
<property name="unauthorizedUrl" value="/error" />
<property name="filterChainDefinitions">
<value>
<!-- !!! Order matters !!! -->
/authenticate = anon
/login = anon
/logout = anon
/error = anon
/** = authc
</value>
</property>
</bean>
I can get the following working correctly:
#RequestMapping(value="/form")
public String viewPatientForm(Model model, #RequestParam(value="patientId", required=false) Long patientId){
if (!SecurityUtils.getSubject().isPermitted("hc:viewPatient")){
logger.error("Operation not permitted");
throw new AuthorizationException("No Permission");
}
}
But the below doesn't work:
#RequiresPermissions("hc:patientView")
#RequestMapping(value="/form")
public String viewPatientForm(Model model, #RequestParam(value="patientId", required=false) Long patientId){
Am I missing something? Please help.

You were absolutely right. After seeing your comment, I started giving it a thought. Well then I found out that it was NOT an implementation problem with Shiro, but the jar dependecies were not properly configured. Shiro's pom.xml should have dependency for cglib2 too.
So the below changes worked for me :
Include all these four jar files.
aspectjrt-1.6.11.jar,
aspectjweaver-1.6.12.jar,
cglib-2.2.2.jar,
asm-3.3.1.jar,
If you are using maven then :
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.6.11</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.12</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
And finally placing the aop:aspectj-autoproxy in the webApplicationContext.xml
<aop:aspectj-autoproxy proxy-target-class="true"/>
<!-- Annotation, so that it's easier to search controllers/components -->
<context:component-scan base-package="com.pepsey.soft.web.controller"/>
Note : The above two configuration should be placed together in the same spring-webApplicationContext.xml. Otherwise it won’t work. Moreover remove context:annotation-config if you have used it in your config. context:component-scan already scans all annotations.
Once you start testing , set your log4j to debug or (better) trace mode. Whenever you are starting your server you will find somewhere the following entry in your logs :
08:16:24,684 DEBUG AnnotationAwareAspectJAutoProxyCreator:537 -
Creating implicit proxy for bean 'userController' with 0 common
interceptor and 1 specific interceptors

Guess Shiro was built when Spring 2.0 was in place. Shiro’s annotations (RequiresRoles etc…) works well for the spring container managed beans (service layer), but it does not work with #Controller annotation. This is due to the fact that #Controller is being component scanned by spring framework. I used AOP to resolve the issue. Below is the solution which worked for me.
For the below solution to work you have to include the below four jars:
aspectjrt-1.6.11.jar
aspectjweaver-1.6.12.jar
cglib-2.2.2.jar
asm-3.3.1.jar
If you are using maven then below configuration would be helpful.
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.6.11</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.12</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
Below is a controller class
import org.apache.shiro.authz.annotation.RequiresRoles;
#Controller
public class PatientController {
#RequiresRoles(“admin,warden”)
#RequestMapping(value="/form")
public String viewPatientForm(Model model, #RequestParam(value="patientId", required=false) Long patientId){
return “somePatientFormJsp”;
}
}
Create the below Aspect for the annotation (RequiresRoles). You can use the same principle to create pointcuts for RequiresPermission.
import java.util.Arrays;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
#Aspect
#Component
public class WebAuthorizationAspect {
#Before("#target(org.springframework.stereotype.Controller) && #annotation(requiresRoles)")
public void assertAuthorized(JoinPoint jp, RequiresRoles requiresRoles) {
SecurityUtils.getSubject().checkRoles(Arrays.asList(requiresRoles.value()));
}
}
In your spring-webApplicationContext.xml wherever you have mentioned
<aop:aspectj-autoproxy proxy-target-class="true"/>
<!-- Annotation, so that it's easier to search controllers/components -->
<context:component-scan base-package="com.example.controller"/>
Note : The above two configuration should be placed together in the same spring-webApplicationContext.xml. Otherwise it won’t work. Moreover remove context:annotation-config if you have used it in your config. context:component-scan already scans all annotations.

If you're avoiding Spring XML and using primarily Java and annotation configuration, the easiest way to fix this is to add
#Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
to all your #Controller classes. You need cglib on the classpath.

I have only used spring-hibernate example from sample. To use annotations like #RequiresPermissions and others I tried configuration from shiro manual, configuration from this post, but I was either unsuccessful to compile or run the valid urls. So I only commented all the #RequiresPermissions from ManageUserController and started to use it in service implementation. E.g In DefaultUserService in getAllUsers method I added the annotation #RequiresPermissions("user:manage"). Magically now the application works as expected. Whenever the url manageUsers is called it displays the list page if the user has role user:manage and throws the user to /unauthorized if the user don't have that permission.
I have even configured the application to use mysql instead. To make the permissions independent of roles according to new RBAC(http://www.stormpath.com/blog/new-rbac-resource-based-access-control) I have created a new class called Permission as
#Entity
#Table(name = "permissions")
#Cache(usage= CacheConcurrencyStrategy.READ_WRITE)
public class Permission {
#Id
#GeneratedValue
private Long id;
private String element;
private String description;
// setter and getter
Now Role class is configured as
#CollectionOfElements
#JoinTable(name="roles_permissions")
#Cache(usage=CacheConcurrencyStrategy.READ_WRITE)
public Set<Permission> getPermissions() {
return permissions;
}
And finally SampleRealm as
for (Role role : user.getRoles()) {
info.addRole(role.getName());
System.out.println("Roles " + role.getName());
// Get permissions first
Set<Permission> permissions = role.getPermissions();
Set<String> permissionsStrings = new HashSet<String>();
for (Permission permission : permissions) {
permissionsStrings.add(permission.getelement());
System.out
.println("Permissions " + permission.getelement());
}
info.addStringPermissions(permissionsStrings);
}
It creates five tables as
| permissions |
| roles |
| roles_permissions |
| users |
| users_roles |
And permissions is independent of any other. According to new RBAC you have both ways (explicit and implicit) way of authorising resources.

You need to write the AuthorizationAttributeSourceAdvisor to enable Shiro's annotations bean as per the Shiro documentation
If you have written ShiroConfiguration class, make sure you include this:
#Bean(name = "lifecycleBeanPostProcessor")
public LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
#Bean
#ConditionalOnMissingBean
public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor(DefaultSecurityManager securityManager) {
// This is to enable Shiro's security annotations
AuthorizationAttributeSourceAdvisor sourceAdvisor = new AuthorizationAttributeSourceAdvisor();
sourceAdvisor.setSecurityManager(securityManager);
return sourceAdvisor;
}
#ConditionalOnMissingBean
#Bean(name = "defaultAdvisorAutoProxyCreator")
#DependsOn("lifecycleBeanPostProcessor")
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator proxyCreator = new DefaultAdvisorAutoProxyCreator();
proxyCreator.setProxyTargetClass(true);
return proxyCreator;
}
Example ShiroConfiguration on Github

I had the same problem. My fix was changing my jersey version from 2.2 to 2.22.2 and all #RequiresPermissions worked on my controllers.

Related

How do I get my Spring Aspect to kick in for a #Valid annotation on a service method?

We're using Spring 3.2.11.RELEASE and Maven 3.0.3. I'm trying to set up validation of a parameter being passed into a service method. The method is below. Notice the #Valid annotation.
package org.mainco.subco.mypck.service;
#Service
#RemoteProxy
#Transactional
public class MypckServiceImpl implements MypckService {
#RemoteMethod
#Override
public String myMethod(#Valid final MyObjectDto request) {
// ...
}
}
Here is the aspect I have set up to help validate the object:
#Aspect
#Component
public class MyObjectValidatingAspect extends AbstractDWRAspectValidator<MyObjectDto>
{
#Before("execution(* org.mainco.subco.mypck.service.MypckService.myMethod(..))")
public void validateBefore(JoinPoint jp)
{
errors = new ArrayList<String>();
final MyObjectDto request = validate(jp);
validateMyObject(request);
throwErrors();
} // validateBefore
This is in included in my application context file:
<global-method-security pre-post-annotations="enabled">
</global-method-security>
<aop:aspectj-autoproxy/>
And this is what I've included in the Maven pom.xml file:
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.2</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.2</version>
</dependency>
Unfortunately when the method is invoked, the aspectj's validateBefore is never called. What else do I need to do so that this gets invoked?
Since Spring 3.1 there is the MethodValidationInterceptor which basically does what you want to achieve yourself. To have this interceptor applied the only thing you need to do is to register a MethodValidationPostProcessor in your application context.
By default it will check for the #Validated annotation from Spring but you can instruct it to scan for the #Valid annotation.
<bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor">
<property name="validatedAnnotationType" value="javax.validation.Valid" />
<property name="validator" ref="refToYOurLocalValidatorFactoryBean" />
</bean>
If you don't specify a validator the default JSR-303 validator mechanism will be used (or the more hibernate specific one if that is available). But I can imagine you want to reuse the already configured instance.

MongoDB - how to handle write failures during primary re-election with Spring?

I configured my MongoDB replica set with Spring, and I'm trying to test the auto-failover.
I know that if the primary goes down, it takes a few seconds for a new primary to be elected, so in that time period, all writes will fail.
I have a test application that writes to the db every 1 sec, and when I take down the primary, I get a java.io.IOException (because there's no primary to write to). If I restart my application the writes are executed without a problem to the new primary.
I thought that the MongoDB Java driver can handle those cases using retries (was I wrong?), but I was unable to configure Spring to do that, so I'd appriciate some help. :)
My configuration is like so:
<mongo:mongo id="mongo" replica-set="host1:27017,host2:27017,host3:27017">
<mongo:options
connections-per-host="8"
threads-allowed-to-block-for-connection-multiplier="4"
connect-timeout="1000"
max-wait-time="1500"
auto-connect-retry="true"
socket-keep-alive="true"
socket-timeout="1500"
slave-ok="true"
write-number="1"
write-timeout="0"
write-fsync="true"/>
</mongo:mongo>
<mongo:repositories base-package="my.repositories" />
<mongo:db-factory dbname="my_db" mongo-ref="mongo" />
<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
<constructor-arg name="mongoDbFactory" ref="mongoDbFactory" />
</bean>
Thanks!
Here is an initial stab at a spring aop/spring retry custom RetryPolicy for generic retry in various circumstances.
This is quite brittle (as it uses exception messages, etc which are subject to change). I would recommend robust testing, and definitely repeating on change of MongoDB and/or java driver version.
Firstly, maven dependancies used:
<dependencies>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<version>2.11.3</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
<version>1.3.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.2</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.6.2</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>1.6.2</version>
</dependency>
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<version>1.0.3.RELEASE</version>
</dependency>
</dependencies>
Second, a custom org.springframework.retry.RetryPolicy
import org.springframework.retry.RetryContext;
import org.springframework.retry.policy.SimpleRetryPolicy;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;
public class CustomMongoDBRetryPolicy extends SimpleRetryPolicy {
private static final Logger logger = Logger.getLogger(CustomMongoDBRetryPolicy.class.getName());
public CustomMongoDBRetryPolicy(int maxAttempts) {
super(maxAttempts, createRetryableExceptions(), true);
}
private static Map<Class<? extends Throwable>, Boolean> createRetryableExceptions() {
HashMap<Class<? extends Throwable>, Boolean> classBooleanHashMap = new HashMap<Class<? extends Throwable>, Boolean>();
classBooleanHashMap.put(org.springframework.dao.DataAccessResourceFailureException.class, true);
classBooleanHashMap.put(org.springframework.data.mongodb.UncategorizedMongoDbException.class, true);
classBooleanHashMap.put(com.mongodb.MongoException.class, true);
classBooleanHashMap.put(java.net.ConnectException.class, true);
return classBooleanHashMap;
}
#Override
public boolean canRetry(RetryContext context) {
boolean retry = super.canRetry(context);
if (retry) {
#SuppressWarnings("ThrowableResultOfMethodCallIgnored")
Throwable lastThrowable = context.getLastThrowable();
if (lastThrowable != null) {
String message = lastThrowable.getMessage();
Throwable cause = lastThrowable.getCause();
if (message != null) {
if (message.startsWith("No replica set members available in")) {
logger.info("Retrying because no replica set members available. "+message);
return true;
}
if (message.startsWith("not talking to master and retries used up")) {
logger.info("Retrying because no master. "+message);
return true;
}
if (message.startsWith("can't find a master")) {
logger.info("Retrying because no master. "+message);
return true;
}
if (message.matches("Read operation to server [^\\s]* failed on database .*")) {
logger.info("Retrying because read operation failed. "+message);
return true;
}
}
if (cause != null) {
String causeMessage = cause.getMessage();
if (causeMessage != null) {
if (causeMessage.startsWith("Connection refused")) {
logger.info("Retrying because connection not available. "+causeMessage+"("+message+")");
return true;
}
}
}
logger.info("Not retrying. "+message+" "+lastThrowable.getClass().getName());
return false;
}
}
return retry;
}
}
Finally, tie into Dao using spring AOP
<aop:config proxy-target-class="false">
<aop:pointcut id="retry"
expression="execution(* IMyDao.count(..))" />
<aop:pointcut id="retry2"
expression="execution(* IMyDao.insert(..))" />
<aop:advisor pointcut-ref="retry"
advice-ref="retryAdvice" order="-1"/>
<aop:advisor pointcut-ref="retry2"
advice-ref="retryAdvice" order="-1"/>
</aop:config>
The following combines org.springframework.retry.backoff.ExponentialBackOffPolicy, to delay retries, org.springframework.retry.policy.TimeoutRetryPolicy, to limit retry time and the CustomMongoDBRetryPolicy, which retries what seems to be retry-able...
<bean id="retryAdvice"
class="org.springframework.retry.interceptor.RetryOperationsInterceptor">
<property name="retryOperations">
<bean class="org.springframework.retry.support.RetryTemplate">
<property name="retryPolicy">
<bean class="org.springframework.retry.policy.CompositeRetryPolicy">
<property name="optimistic" value="false"/>
<property name="policies">
<set>
<bean class="org.springframework.retry.policy.TimeoutRetryPolicy">
<property name="timeout" value="20000"/>
</bean>
<bean class="CustomMongoDBRetryPolicy">
<constructor-arg value="100"/>
</bean>
</set>
</property>
</bean>
</property>
<property name="listeners">
<set>
<bean class="MyRetryListener"/>
</set>
</property>
<property name="backOffPolicy">
<bean class="org.springframework.retry.backoff.ExponentialBackOffPolicy">
<property name="initialInterval" value="500"/>
<property name="maxInterval" value="8000"/>
<property name="multiplier" value="1.5"/>
</bean>
</property>
</bean>
</property>
</bean>
Ive tested this with various scenarios, and it seem to be handling most pretty well. But whether it will work for a particular application, needs to be answered on a case by case basis.
Initial replicaset start - regular autoreconnect handles before the servers are listening, this handles prior to primary election - all invisible to the application (bar a long lag)
Killing the primary - write operation in progress fails to the application, subsequent retry
Stepping down the primary, shutting down the primary - as killing the primary.
Full replicaset restart (if fast enough)
Hope this helps
you set socket-timeout="1500". you should not set any socket timeout. Defaults to 0 (infinite time).

SPRING, JPA(EclipseLink) with JTA Transaction Manager(JBoss 7) - NOT Committing to Database

I have created an example - SPRING, JPA(EclipseLink persistence provider) with JTA Transaction Manager(JBoss 7). I have observed that all the data in database is being shown in UI properly for the read operations. But when it comes to save/update or delete operation the services layer is not committing the work to database. No exception is caught(I have checked the console/log too and also debugged the code where I can see entityManager.persist/remove is being invoked without any exception).
--Code Listing--
1. Datasource configuration in standalone.xml
<datasource jta="true" jndi-name="java:/mysql_customerdb3" pool-name="mysql_customerdb3_pool" enabled="true" use-java-context="true" use-ccm="true">
<connection-url>jdbc:mysql://localhost:3306/customerdb</connection-url>
<driver>mysql</driver>
<security>
<user-name>root</user-name>
<password>root</password>
</security>
<statement>
<prepared-statement-cache-size>10</prepared-statement-cache-size>
<share-prepared-statements>true</share-prepared-statements>
</statement>
</datasource>
<drivers>
<driver name="mysql" module="com.mysql">
<driver-class>com.mysql.jdbc.Driver</driver-class>
<xa-datasource-class>com.mysql.jdbc.jdbc2.optional.MysqlXADataSource</xa-datasource-class>
</driver>
<driver name="h2" module="com.h2database.h2">
<xa-datasource-class>org.h2.jdbcx.JdbcDataSource</xa-datasource-class>
</driver>
</drivers>
Database driver configuration in module.xml
persistence.xml
org.eclipse.persistence.jpa.PersistenceProvider
java:/mysql_customerdb3
com.springforbeginners.model.Customer
customerdispatcher-servlet.xml
<context:annotation-config />
<context:component-scan base-package="com.springforbeginners" />
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" >
<property name="loadTimeWeaver" ref="loadTimeWeaver" />
<property name="persistenceXmlLocation" value="classpath:META-INF/persistence.xml" />
</bean>
<bean id="loadTimeWeaver" class="org.springframework.instrument.classloading.SimpleLoadTimeWeaver" >
</bean>
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="transactionManagerName" value="java:jboss/TransactionManager"/>
<property name="userTransactionName" value="java:jboss/UserTransaction"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
CustomerServiceImpl.java
package com.springforbeginners.service;
import com.springforbeginners.dao.CustomerDAO;
import com.springforbeginners.model.Customer;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
#Service
public class CustomerServiceImpl implements CustomerService {
#Autowired
private CustomerDAO customerDAO;
#Transactional
#Override
public void addCustomer(Customer customer) {
customerDAO.addCustomer(customer);
}
#Transactional
#Override
public List<Customer> listCustomer() {
return customerDAO.listCustomer();
}
#Transactional
#Override
public void removeCustomer(Integer customerId) {
customerDAO.removeCustomer(customerId);
}
}
CustomerDAOImpl.java
package com.springforbeginners.dao;
import com.springforbeginners.model.Customer;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.springframework.stereotype.Repository;
#Repository
public class CustomerDAOImpl implements CustomerDAO {
#PersistenceContext(unitName="CustomerDetailsPU3")
private EntityManager entityManager;
#Override
public void addCustomer(Customer customer) {
entityManager.persist(customer);
}
#Override
public List<Customer> listCustomer() {
return entityManager.createQuery("select c from Customer c", Customer.class).getResultList();
}
#Override
public void removeCustomer(Integer customerId) {
Customer customer = (Customer) entityManager.getReference(Customer.class, customerId);
if (null != customer) {
entityManager.remove(customer);
}
}
}
I do not know what and where exactly is something missing. But with the above code the read operations are working as expected. Problem is with save operations. I have converted the above example to use non-JTA datasource(also modified standalone.xml for jta=false) and to use JpaTransactionManager as below
With non-JTA datasource and 'org.springframework.orm.jpa.JpaTransactionManager' all operations(read as well as save/update/delete) are working fine.
But the JTA version of my example is not working as expected(save operations not committing work to database). Any help/pointers appreciated.
Thanks
Prakash
James,
I will be running this application on JBoss. But one datasource on JBoss and other on Glassfish and transaction should span save operation on both datasources simultaneously. This is what I am trying to achieve. I have a web application including spring for service(data) layer currently running on JBoss.
As you said earlier - I will have two persistence.xmls one for JBoss and one for Glassfish. As I am doing this first time I was/am in doubt whether the transaction(that spans two datasources on different servers-in this case JBoss and Glassfish), can this be executed entirely by JBoss(in case if the entire business logic resides in serviceImpl class deployed on JBoss)? In this case I will be using JBoss transaction manager( property name="transactionManagerName" value="java:jboss/TransactionManager" ). Is this sufficient or do I need to similarly have Glassfish transaction manager too? Sorry if this has created the confusion.
Another question from me was that is there a provision for speifying jndi ports in persistence.xml/anywhere else?(Definitely I will have two different persistence.xmls and I will mention the target server as JBoss in one and as Glassfish in another).
Do we have a technique in spring by which business logic can be distributed across different servers like JBoss/Glassfish and still under one single transatcion? I did not know if this can be an option. Were u talking about this scenario in which it will require two different deployment scripts one for each server?
Thanks
Prakash
What is your persistence.xml?
Since you are using JTA, you must define the "eclipselink.target-server"="JBoss"
My persistence.xml(modified) now looks like below. Added target server property in persistence.xml. This solved the problem.
<persistence-unit name="CustomerDetailsPU3" transaction-type="JTA">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<jta-data-source>java:/mysql_customerdb3</jta-data-source>
<class>com.springforbeginners.model.Customer</class>
<properties>
<property name="eclipselink.target-server" value="JBoss" />
</properties>
</persistence-unit>
Thanks
Prakash

Why can't my Spring MVC controller mapping reference another mapping in the same controller?

I have the following controller mapped as
#Controller( value = "stockToStoreController" )
#RequestMapping("/stsr")
public class StockToStoreController extends BaseController {...}
I have a delete mapping
#Transactional(propagation = Propagation.REQUIRED)
#RequestMapping(value = "/delete")
public String delete(#RequestParam("xxxId") long xxxId) {
XXXModel xxxModel = stockToStoreDao.findById(xxxId);
if(xxxModel != null) {
xxxDao.delete(xxxModel);
}
return "/stsr/requery";
}
That mapping looks like this
#SuppressWarnings("unchecked")
#RequestMapping(value = "/requery")
public ModelAndView requery(HttpServletRequest request) {
ModelAndView mav = new ModelAndView("manageStockToStore");
//do stuff
return mav;
}
I try to call another mapping in the return ie., return "/stsr/requery"; I get
the following error:
Uncaught exception thrown in one of the service methods of the servlet: mptstp. Exception thrown : javax.servlet.ServletException: Could not resolve view with name '/stsr/requery' in servlet with name 'xxx'
Question is, do I need to explicitly define this mapping somewhere? I do not have any MappingHandlers defined and my -servlet.xml looks like
<!-- Configures the #Configuration annotation for java configuration -->
<context:annotation-config/>
<!-- Scans the classpath of this application for #Components to deploy as beans -->
<context:component-scan base-package="xxx.testspringmvc.stsr" />
<!-- Configures the #Controller programming model -->
<mvc:annotation-driven />
<!-- Configures resources so they can be used across web modules -->
<mvc:resources mapping="/resources/**" location="/, classpath:/META-INF/public-resources/" />
<!-- Application Message Bundle -->
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basenames" value="classpath:META-INF/public-resources/mptstp-messages, classpath:META-INF/public-resources/mptstp-error-messages, classpath:META-INF/public-resources/stsr/stsr-messages" />
<property name="cacheSeconds" value="0" />
</bean>
<!-- Spring MVC View Resolver -->
<bean id="viewResolver" class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
<property name="basename" value="stsr-views" />
<property name="defaultParentView" value="parentView"/>
</bean>
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean id="urlConfiguredSiteIdInterceptor" class="xxx.testspringmvc.stsr.interceptor.UrlConfiguredSiteIdInterceptor">
<property name="siteIdConfigParamName" value="urlConfiguredSiteId" />
<property name="errorView" value="siteIdNotFound" />
</bean>
</mvc:interceptor>
</mvc:interceptors>
Any help from you guys would be greatly appreciated.
Two options:
you can redirect return "redirect:/stsr/requery"
you can directly invoke the other method: return requery(request);
It's looking for a view and you want a mapping. You have to use a redirect:
return "redirect:/stsr/requery";

Sharing Spring Security Configuration Between Applications

I'm brand spanking new at Spring and have gotten a majority of the knowledge I do have from the Spring Recipes book from Apress.
I've got LDAP authentication working with Spring Security within one webapp. I would like to rip out my application context beans and properties files from this one webapp, however, and somehow externalize them so that all of our webapps can reference the same beans. So when we need to change something (like the ldapuser or the ldap urls), we change it in one place and the rest of the apps just know.
UPDATE
I've implemented Reloadable Spring Properties which is reloading properties when the files they come from are touched. I am using encrypted properties, however, so below is class I created on top of the Reloadable Spring Properties ones.
ReloadingEncryptablePropertyPlaceholderConfigurer.java
package;
import java.util.Properties;
import java.util.Set;
import org.apache.commons.lang.Validate;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jasypt.encryption.StringEncryptor;
import org.jasypt.util.text.TextEncryptor;
import org.jasypt.properties.PropertyValueEncryptionUtils;
import org.springframework.beans.factory.BeanDefinitionStoreException;
public class ReloadingEncryptablePropertyPlaceholderConfigurer extends ReloadingPropertyPlaceholderConfigurer {
protected final Log logger = LogFactory.getLog(getClass());
private final StringEncryptor stringEncryptor;
private final TextEncryptor textEncryptor;
public ReloadingEncryptablePropertyPlaceholderConfigurer(TextEncryptor textEncryptor) {
super();
logger.info("Creating configurer with TextEncryptor");
Validate.notNull(textEncryptor, "Encryptor cannot be null");
this.stringEncryptor = null;
this.textEncryptor = textEncryptor;
}
public ReloadingEncryptablePropertyPlaceholderConfigurer(StringEncryptor stringEncryptor) {
super();
logger.info("Creating configurer with StringEncryptor");
Validate.notNull(stringEncryptor, "Encryptor cannot be null");
this.stringEncryptor = stringEncryptor;
this.textEncryptor = null;
}
#Override
protected String convertPropertyValue(String originalValue) {
if (!PropertyValueEncryptionUtils.isEncryptedValue(originalValue)) {
return originalValue;
}
if (this.stringEncryptor != null) {
return PropertyValueEncryptionUtils.decrypt(originalValue, this.stringEncryptor);
}
return PropertyValueEncryptionUtils.decrypt(originalValue, this.textEncryptor);
}
#Override
protected String parseStringValue(String strVal, Properties props, Set visitedPlaceholders) throws BeanDefinitionStoreException {
return convertPropertyValue(super.parseStringValue(strVal, props, visitedPlaceholders));
}
}
And here's how I use it in my securityContext.xml:
<bean id="securityContextSource" class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
<constructor-arg value="ldaps://ldapserver" />
<property name="urls" value="#{ldap.urls}" />
</bean>
<bean id="timer" class="org.springframework.scheduling.timer.TimerFactoryBean">
<property name="scheduledTimerTasks">
<bean id="reloadProperties" class="org.springframework.scheduling.timer.ScheduledTimerTask">
<property name="period" value="1000"/>
<property name="runnable">
<bean class="ReloadConfiguration">
<property name="reconfigurableBeans">
<list>
<ref bean="configproperties"/>
</list>
</property>
</bean>
</property>
</bean>
</property>
</bean>
<bean id="configproperties" class="ReloadablePropertiesFactoryBean">
<property name="location" value="classpath:ldap.properties"/>
</bean>
<bean id="ldapPropertyConfigurer" class="ReloadingEncryptablePropertyPlaceholderConfigurer">
<constructor-arg ref="configurationEncryptor" />
<property name="ignoreUnresolvablePlaceholders" value="true" />
<property name="properties" ref="configproperties"/>
</bean>
<bean id="jasyptConfig" class="org.jasypt.encryption.pbe.config.SimpleStringPBEConfig">
<property name="algorithm" value="PBEWithMD5AndTripleDES" />
<property name="password" value="########" />
</bean>
<bean id="configurationEncryptor" class="org.jasypt.encryption.pbe.StandardPBEStringEncryptor">
<property name="config" ref="jasyptConfig" />
</bean>
How about:
Writing a method that returns a list
of LDAP servers - reading from a
database table or property files
expose this wethod via jndi and use it to inject a list of the servers into your spring config
If you need the ldap servers to be refreshed dynamically you could have a job poll for changes periodically or else have an admin webpage or jmx bean to trigger the update. Be careful of concurrency isses for both these methods (something reading the list while you are updating)
Wouldn't that be Spring Security? It can deal with LDAPs. And if you make it one security service that everyone uses, wouldn't that be the way to manage it?

Resources