I'm unit testing my DAOs using spring. I am using JPA + Hibernate.
For my tests i'm using the following hsqldb version:
<dependency>
<groupId>hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>1.8.0.10</version>
<scope>test</scope>
</dependency>
And here's my persistence.xml for my unit tests:
<persistence-unit name="unit-test-pu" transaction-type="RESOURCE_LOCAL">
<properties>
<property name="javax.persistence.jdbc.driver" value="org.hsqldb.jdbcDriver"/>
<property name="javax.persistence.jdbc.user" value="sa"/>
<property name="javax.persistence.jdbc.password" value=""/>
<property name="javax.persistence.jdbc.url" value="jdbc:hsqldb:mem:testdb"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect"/>
<property name="hibernate.archive.autodetection" value="class"/>
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.use_sql_comments" value="true"/>
<property name="hibernate.format_sql" value="true"/>
<property name="hibernate.hbm2ddl.auto" value="create"/>
</properties>
</persistence-unit>
This is my test class:
#ContextConfiguration("/spring/test-context.xml")
#TestExecutionListeners({TransactionalTestExecutionListener.class})
#Transactional
public class BaseDaoTestCase extends AbstractJUnit4SpringContextTests {
#Autowired
private MockEntityDao dao;
#Test
public void testSave_success() {
MockEntity e = new MockEntity();
dao.save(e);
assertNotNull(e.getId());
}
}
Note: the idea is to test my DAOs against an in memory HSQLDB instance. Each test method runs within a transaction managed by spring.
Unfortunately, when i run this i get the following exception from hibernate:
Caused by: org.hibernate.exception.GenericJDBCException: This function is not supported
at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:54)
at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:125)
at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:110)
at org.hibernate.engine.jdbc.internal.proxy.ConnectionProxyHandler.continueInvocation(ConnectionProxyHandler.java:146)
at org.hibernate.engine.jdbc.internal.proxy.AbstractProxyHandler.invoke(AbstractProxyHandler.java:81)
at $Proxy23.prepareStatement(Unknown Source)
at org.hibernate.engine.jdbc.internal.StatementPreparerImpl$2.doPrepare(StatementPreparerImpl.java:105)
at org.hibernate.engine.jdbc.internal.StatementPreparerImpl$StatementPreparationTemplate.prepareStatement(StatementPreparerImpl.java:166)
at org.hibernate.engine.jdbc.internal.StatementPreparerImpl.prepareStatement(StatementPreparerImpl.java:103)
at org.hibernate.id.insert.AbstractSelectingDelegate.performInsert(AbstractSelectingDelegate.java:55)
at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2764)
at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3275)
at org.hibernate.action.internal.EntityIdentityInsertAction.execute(EntityIdentityInsertAction.java:81)
at org.hibernate.engine.spi.ActionQueue.execute(ActionQueue.java:362)
at org.hibernate.engine.spi.ActionQueue.addResolvedEntityInsertAction(ActionQueue.java:203)
at org.hibernate.engine.spi.ActionQueue.addInsertAction(ActionQueue.java:183)
at org.hibernate.engine.spi.ActionQueue.addAction(ActionQueue.java:167)
at org.hibernate.event.internal.AbstractSaveEventListener.addInsertAction(AbstractSaveEventListener.java:320)
at org.hibernate.event.internal.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:287)
at org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:193)
at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:126)
at org.hibernate.ejb.event.EJB3PersistEventListener.saveWithGeneratedId(EJB3PersistEventListener.java:78)
at org.hibernate.event.internal.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:208)
at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:151)
at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:78)
at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:757)
at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:732)
at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:736)
at org.hibernate.ejb.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:854)
... 42 more
Caused by: java.sql.SQLException: This function is not supported
at org.hsqldb.jdbc.Util.sqlException(Unknown Source)
at org.hsqldb.jdbc.Util.notSupported(Unknown Source)
at org.hsqldb.jdbc.jdbcConnection.prepareStatement(Unknown Source)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:616)
at org.hibernate.engine.jdbc.internal.proxy.ConnectionProxyHandler.continueInvocation(ConnectionProxyHandler.java:138)
... 67 more
The implementation of my DAO's save() method is as follows:
entityManager.persist(entity);
I'm using the following hibernate version:
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>4.1.0.Final</version>
<scope>compile</scope>
</dependency>
Does anyone have a clue on what's going on?
I had similar Problem. After I update my dependencies (especially hsqldb) to:
<hibernate-core-version>4.1.5.Final</hibernate-core-version>
<spring.version>3.1.2.RELEASE</spring.version>
<hsqldb.version>2.2.8</hsqldb.version>
...
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>${hsqldb.version}</version>
</dependency>
my problem is resolved.
Which mocking framework are you using? I'm just learning to work with mocks so this is just a wild guess but make sure you've done everything necessary to initialize the mock. Maybe the problem has something to do with how the test is being setup.
I'm having the same problem working with JPA + Hibernate + HSQLDB, but not spring.
Here is my code:
EntityManagerFactory emf = Persistence.createEntityManagerFactory("persistenceUnit");
EntityManager em = emf.createEntityManager();
EntityTransaction tr = em.getTransaction();
tr.begin();
em.persist(new MyEntity());
tr.commit();
em.close();
emf.close();
That throws me the same This function is not supported exception, but if I do it without the transaction, it doesn't. Like this:
EntityManagerFactory emf = Persistence.createEntityManagerFactory("persistenceUnit");
EntityManager em = emf.createEntityManager();
em.persist(new MyEntity());
em.close();
emf.close();
The problem now, is that I don't see the changes reflected on database.
Related
I am trying to create a bundle which uses h2 database . I am using felix maven plugin .
Where I have the felix Configuration is like
<Import-Package>*,org.h2.Driver,org.h2.*</Import-Package>
<DynamicImport-Package>*</DynamicImport-Package>
But i am getting an exception like this
org.osgi.service.blueprint.container.ComponentDefinitionException: Error setting property: PropertyDescriptor <name: driverClassName, getter: null, setter: [class org.springframework.jdbc.datasource.DriverManagerDataSource.setDriverClassName(class java.lang.String)]
at org.apache.aries.blueprint.container.BeanRecipe.setProperty(BeanRecipe.java:941)[21:org.apache.aries.blueprint.core:1.4.2]
at org.apache.aries.blueprint.container.BeanRecipe.setProperties(BeanRecipe.java:907)[21:org.apache.aries.blueprint.core:1.4.2]
at org.apache.aries.blueprint.container.BeanRecipe.setProperties(BeanRecipe.java:888)[21:org.apache.aries.blueprint.core:1.4.2]
at org.apache.aries.blueprint.container.BeanRecipe.internalCreate2(BeanRecipe.java:820)[21:org.apache.aries.blueprint.core:1.4.2]
at org.apache.aries.blueprint.container.BeanRecipe.internalCreate(BeanRecipe.java:787)[21:org.apache.aries.blueprint.core:1.4.2]
at org.apache.aries.blueprint.di.AbstractRecipe$1.call(AbstractRecipe.java:79)[21:org.apache.aries.blueprint.core:1.4.2]
at java.util.concurrent.FutureTask.run(FutureTask.java:266)[:1.8.0_60]
at org.apache.aries.blueprint.di.AbstractRecipe.create(AbstractRecipe.java:88)[21:org.apache.aries.blueprint.core:1.4.2]
at org.apache.aries.blueprint.container.BlueprintRepository.createInstances(BlueprintRepository.java:245)[21:org.apache.aries.blueprint.core:1.4.2]
at org.apache.aries.blueprint.container.BlueprintRepository.createAll(BlueprintRepository.java:183)[21:org.apache.aries.blueprint.core:1.4.2]
at org.apache.aries.blueprint.container.BlueprintContainerImpl.instantiateEagerComponents(BlueprintContainerImpl.java:682)[21:org.apache.aries.blueprint.core:1.4.2]
at org.apache.aries.blueprint.container.BlueprintContainerImpl.doRun(BlueprintContainerImpl.java:377)[21:org.apache.aries.blueprint.core:1.4.2]
at org.apache.aries.blueprint.container.BlueprintContainerImpl.run(BlueprintContainerImpl.java:269)[21:org.apache.aries.blueprint.core:1.4.2]
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)[:1.8.0_60]
at java.util.concurrent.FutureTask.run(FutureTask.java:266)[:1.8.0_60]
at org.apache.aries.blueprint.container.ExecutorServiceWrapper.run(ExecutorServiceWrapper.java:106)[21:org.apache.aries.blueprint.core:1.4.2]
at org.apache.aries.blueprint.utils.threading.impl.DiscardableRunnable.run(DiscardableRunnable.java:48)[21:org.apache.aries.blueprint.core:1.4.2]
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)[:1.8.0_60]
at java.util.concurrent.FutureTask.run(FutureTask.java:266)[:1.8.0_60]
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)[:1.8.0_60]
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)[:1.8.0_60]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)[:1.8.0_60]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)[:1.8.0_60]
at java.lang.Thread.run(Thread.java:745)[:1.8.0_60]
Caused by: java.lang.IllegalStateException: Could not load JDBC driver class [org.h2.Driver]
at org.springframework.jdbc.datasource.DriverManagerDataSource.setDriverClassName(DriverManagerDataSource.java:150)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)[:1.8.0_60]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)[:1.8.0_60]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)[:1.8.0_60]
at java.lang.reflect.Method.invoke(Method.java:497)[:1.8.0_60]
at org.apache.aries.blueprint.utils.ReflectionUtils$MethodPropertyDescriptor.internalSet(ReflectionUtils.java:628)[21:org.apache.aries.blueprint.core:1.4.2]
at org.apache.aries.blueprint.utils.ReflectionUtils$PropertyDescriptor.set(ReflectionUtils.java:378)[21:org.apache.aries.blueprint.core:1.4.2]
at org.apache.aries.blueprint.container.BeanRecipe.setProperty(BeanRecipe.java:939)[21:org.apache.aries.blueprint.core:1.4.2]
... 23 more
Caused by: java.lang.ClassNotFoundException: org.h2.Driver
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)[:1.8.0_60]
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)[:1.8.0_60]
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)[:1.8.0_60]
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)[:1.8.0_60]
at java.lang.Class.forName0(Native Method)[:1.8.0_60]
at java.lang.Class.forName(Class.java:348)[:1.8.0_60]
at org.springframework.jdbc.datasource.DriverManagerDataSource.setDriverClassName(DriverManagerDataSource.java:147)
... 30 more
But i am doing the same with the mysql database and its works fine. But i want to use h2 in my bundle and i am facing the same problem with hsql database also. looks like i am missing something , please help me .
Your Exception has nothing to do with the felix plugin , the dependecy jars and the necessary properties for the datasource are not present .. add the properties to your datasource like below.
<property name="url" value="jdbc:h2:<url>"/>
<property name="username" value="user"/>
<property name="password" value=""/>
<property name="driverClassName" value="org.h2.Driver"/>
and also add the dependency
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>your version</version>
</dependency>
Versions in use:
Spring 4.1.6.RELEASE, Hibernate 4.3.10.Final, Atomikos 3.9.26
We are in the process of upgrading our main webapp to Hibernate 4. We mainly use HibernateTemplate and JdbcTemplate for access to multiple databases (DB2 and Oracle) with Atomikos as JTA-TransactionManager.
The problem: While using only HibernateTemplate or only JdbcTemplates in a single transaction works fine, using JdbcTemplate and HibernateTemplate together in one transaction causes StaleStateExceptions in certain cases.
Here is an example where the problem occurs - the code is wrapped in a TransactionalProxyFactoryBean with PROPAGATION_REQUIRED:
public class MyServiceImpl extends HibernateDaoSupport implements MyService {
private static final Log log = LogFactory.getLog(MyServiceImpl.class);
private JdbcTemplate jdbcTemplate;
#Override
public void execute() {
// save new entity instance with HibernateTemplate
MyEntity e = new MyEntity();
e.setMyProperty("first value");
getHibernateTemplate().save(e);
// use JdbcTemplate to access DB
String sql = "select * from my_table";
getJdbcTemplate().query(sql, new RowCallbackHandler() {
#Override
public void processRow(ResultSet rs) throws SQLException {
// process rows
}
});
// update entity instance with HibernateTemplate
e.setMyProperty("second value");
getHibernateTemplate().saveOrUpdate(e);
// make sure the flush occurs immediately. This is needed in to demonstrate the problem. (Otherwise the property UPDATE would be cached and issued on commit, just after Spring closed the connection used for the JdbcTemplate and the problem would not show)
getHibernateTemplate().flush();
}
public JdbcTemplate getJdbcTemplate() {
return jdbcTemplate;
}
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
}
Our conclusions: The exception is basically caused by different ways HibernateTemplate and JdbcTemplate accquire and release the database connection.
The HibernateTemplate directly delegates to Hibernate which uses the connection release mode AFTER_STATEMENT (set by Spring if a JtaTransactionManager is provided). This causes Hibernate to get a connection from the Atomikos connection pool, perform the SQL and close its connection which doesn't close the physical connection but returns it to the connection pool.
The JdbcTemplate uses Spring's DataSourceUtils.getConnection(...) to get a connection from the Atomikos connection pool, performs the SQL and calls DataSourceUtils.releaseConnection(...) which itself doesn't call Connection.close(). The connection isn't closed by Spring in DataSourceUtils.releaseConnection(...) (and in consequence not returned to the connection pool) but bound to the thread for reuse in DataSourceUtils.getConnection(...).
So it seems as if in a JTA context, Spring teaches Hibernate to use connection release mode AFTER_STATEMENT (which is also recommeded by Hibernate for JTA) but behaves totally different in it's DataSourceUtils.
In detail, we tracked down the cause like following:
The StaleStateException is thrown because the UPDATE-Statement for setting "second value" at the entity does not affect any row in the database.
This is because the UPDATE-Statement happens on another connection than the INSERT-Statement.
This is because the original connection used by the INSERT-Statement is still considered in use by the connection pool.
This is because close() is never called on the first connection after it was used for the JdbcTemplate.
This is because DataSourceUtils.releaseConnection(...) which is called by the JdbcTemplate when finished doesn't call Connection.close() in a JTA-Transaction-Context.
Things we tried and failed at:
Make Hibernate use AFTER_TRANSACTION or ON_CLOSE as connection release mode - prevented by Spring as SpringJtaSessionContext with it's AFTER_STATEMENT is hardcoded.
Configure Spring close the DB connection on connection release.
What are we doing wrong?
Any configuration we forgot?
Is it a Spring/Hibernate problem at all or should the Atomikos connection pool behave differently by not waiting for a call to Connection.close() before making the connection available again?
Thanks a lot for your help!
Spring context for Hibernate and JTA configuration:
<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource">
<ref bean="dataSource" />
</property>
<property name="jtaTransactionManager" ref="transactionManager" />
<property name="hibernateProperties">
<props>
<!-- Stripped down configuration for the toy project to reproduce the problem -->
<prop key="hibernate.cache.use_second_level_cache">false</prop>
<prop key="hibernate.dialect">com.company.DB2Dialect</prop>
<!-- hibernate.transaction.factory_class and hibernate.transaction.jta.platform are implicitly set by setting the jtaTransactionManager property -->
<!-- Properties wie normally use in production
<prop key="hibernate.dialect">com.company.DB2Dialect</prop>
<prop key="hibernate.cache.use_query_cache">false</prop>
<prop key="hibernate.cache.use_second_level_cache">true</prop>
<prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</prop>
<prop key="hibernate.order_inserts">true</prop>
<prop key="hibernate.order_updates">true</prop>
<prop key="hibernate.generate_statistics">false</prop>
<prop key="hibernate.use_outer_join">true</prop>
<prop key="hibernate.jdbc.batch_versioned_data">true</prop>
<prop key="hibernate.bytecode.use_reflection_optimizer">true</prop>
<prop key="hibernate.jdbc.batch_size">100</prop> -->
</props>
</property>
<property name="mappingLocations">
<list>
<value>classpath*:**/*.hbm.xml</value>
</list>
</property>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="dataSource"
class="org.springframework.jdbc.datasource.lookup.IsolationLevelDataSourceRouter"
scope="singleton">
<property name="targetDataSources">
<map>
<entry key="ISOLATION_REPEATABLE_READ" value="java:comp/env/jdbc/wawi_rr" />
<entry key="ISOLATION_READ_UNCOMMITTED" value="java:comp/env/jdbc/wawi_ru" />
<entry key="ISOLATION_READ_COMMITTED" value="java:comp/env/jdbc/wawi_rc" />
<entry key="ISOLATION_SERIALIZABLE" value="java:comp/env/jdbc/wawi_s" />
</map>
</property>
<property name="defaultTargetDataSource" value="java:comp/env/jdbc/wawi" />
</bean>
<bean id="transactionManager"
class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="transactionManagerName">
<value>java:comp/env/TransactionManager</value>
</property>
<property name="allowCustomIsolationLevels">
<value>true</value>
</property>
</bean>
Spring context for Service configuration:
<bean id="myService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="target">
<ref bean="myServiceTarget" />
</property>
<property name="transactionManager">
<ref bean="transactionManager" />
</property>
<property name="transactionAttributes">
<props>
<prop key="*">PROPAGATION_REQUIRED,ISOLATION_DEFAULT</prop>
</props>
</property>
</bean>
<bean id="myServiceTarget" class="org.example.MyServiceImpl">
<property name="sessionFactory" ref="sessionFactory" />
<property name="jdbcTemplate" ref="jdbcTemplate" />
</bean>
<bean id="myMBean" class="org.example.MyMBean">
<property name="myService" ref="myService" />
</bean>
Stacktrace:
org.springframework.orm.hibernate4.HibernateOptimisticLockingFailureException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1; nested exception is org.hibernate.StaleStateException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1
at org.springframework.orm.hibernate4.SessionFactoryUtils.convertHibernateAccessException(SessionFactoryUtils.java:205)
at org.springframework.orm.hibernate4.HibernateTemplate.doExecute(HibernateTemplate.java:343)
at org.springframework.orm.hibernate4.HibernateTemplate.executeWithNativeSession(HibernateTemplate.java:308)
at org.springframework.orm.hibernate4.HibernateTemplate.flush(HibernateTemplate.java:837)
at org.example.MyServiceImpl.execute(MyServiceImpl.java:45)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:317)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:281)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
at com.sun.proxy.$Proxy13.execute(Unknown Source)
at org.example.MyMBean.execute(MyMBean.java:13)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at sun.reflect.misc.Trampoline.invoke(MethodUtil.java:75)
at sun.reflect.GeneratedMethodAccessor31.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at sun.reflect.misc.MethodUtil.invoke(MethodUtil.java:279)
at javax.management.modelmbean.RequiredModelMBean$4.run(RequiredModelMBean.java:1245)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76)
at javax.management.modelmbean.RequiredModelMBean.invokeMethod(RequiredModelMBean.java:1239)
at javax.management.modelmbean.RequiredModelMBean.invoke(RequiredModelMBean.java:1077)
at org.springframework.jmx.export.SpringModelMBean.invoke(SpringModelMBean.java:90)
at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:819)
at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:801)
at javax.management.remote.rmi.RMIConnectionImpl.doOperation(RMIConnectionImpl.java:1487)
at javax.management.remote.rmi.RMIConnectionImpl.access$300(RMIConnectionImpl.java:97)
at javax.management.remote.rmi.RMIConnectionImpl$PrivilegedOperation.run(RMIConnectionImpl.java:1328)
at javax.management.remote.rmi.RMIConnectionImpl.doPrivilegedOperation(RMIConnectionImpl.java:1420)
at javax.management.remote.rmi.RMIConnectionImpl.invoke(RMIConnectionImpl.java:848)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:322)
at sun.rmi.transport.Transport$1.run(Transport.java:177)
at sun.rmi.transport.Transport$1.run(Transport.java:174)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.Transport.serviceCall(Transport.java:173)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:556)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:811)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:670)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:724)
Caused by: org.hibernate.StaleStateException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1
at org.hibernate.jdbc.Expectations$BasicExpectation.checkBatched(Expectations.java:81)
at org.hibernate.jdbc.Expectations$BasicExpectation.verifyOutcome(Expectations.java:73)
at org.hibernate.engine.jdbc.batch.internal.NonBatchingBatch.addToBatch(NonBatchingBatch.java:63)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:3281)
at org.hibernate.persister.entity.AbstractEntityPersister.updateOrInsert(AbstractEntityPersister.java:3183)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:3525)
at org.hibernate.action.internal.EntityUpdateAction.execute(EntityUpdateAction.java:159)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:465)
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:351)
at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:350)
at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:56)
at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1258)
at org.springframework.orm.hibernate4.HibernateTemplate$27.doInHibernate(HibernateTemplate.java:840)
at org.springframework.orm.hibernate4.HibernateTemplate.doExecute(HibernateTemplate.java:340)
... 54 more
Any help will be greatly appreciated.
We are working on a web application. Which uses a JAR file (a java maven project) and has been added as a maven dependency in the web application.
Combination of this JAR file and web application itself creating problem.
Both web application and JAR are using Hibernate JPA to interact with database. But both are using 2 different ways for creating/initializing entityManagerFactory.
Web Application uses Spring xml based configuration to initialize entityManagerFactory.
CODE:
persistence.xml code:
<persistence-unit name="org.jbpm.persistence.jpa.local"
transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<mapping-file>META-INF/JBPMorm-JPA2.xml</mapping-file>
<class>org.drools.persistence.info.SessionInfo</class>
<class>org.jbpm.persistence.processinstance.ProcessInstanceInfo</class>
<class>org.drools.persistence.info.WorkItemInfo</class>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.SQLServerDialect" />
<property name="hibernate.max_fetch_depth" value="3" />
<property name="hibernate.show_sql" value="true" />
</properties>
</persistence-unit>
Spring configuration:
<context:component-scan base-package="com.company.rd.core" />
<context:component-scan base-package="com.company.rd.services" />
<jee:jndi-lookup id="testDataSource" jndi-name="java:comp/env/jdbc/SybaseDB" />
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="testDataSource"/>
<property name="defaultTimeout" value="120"></property>
</bean>
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="org.jbpm.persistence.jpa.local" />
<property name="dataSource" ref="testDataSource" />
<property name="jpaDialect" ref="jpaDialect" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
</property>
</bean>
<bean id="entityManager" class="org.springframework.orm.jpa.support.SharedEntityManagerBean">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<bean id="jpaDialect" class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />
<bean id="jpaTransactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
<property name="jpaDialect" ref = "jpaDialect"></property>
<property name="defaultTimeout" value="120"></property>
</bean>
<jee:jndi-lookup id="logDataSource" jndi-name="java:comp/env/jdbc/DRMLOG" />
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
</beans>
And Here is the code to initializing entitymanagerFactory in JAR file.
persistence.xml
<persistence-unit name="codeAuthorization" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<non-jta-data-source>java:/comp/env/jdbc/SybaseDB</non-jta-data-source>
<class>com.company.auth.entity.AuthorizationCode</class>
<class>com.company.auth.entity.UserInvalidAttempt</class>
<class>com.company.auth.entity.AuthorizationProperty</class>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.SQLServerDialect"/>
<property name="hibernate.show_sql" value="true" />
</properties>
</persistence-unit>
And a java file which is injected into Base DAO through spring.
#Service
public class AuthorizationEntityMangerService {
#PersistenceUnit(name = "codeAuthorization")
private EntityManagerFactory entityManagerFactory;
public AuthorizationEntityMangerService() {
entityManagerFactory = Persistence
.createEntityManagerFactory("org.jbpm.persistence.jpa.local");
}
public EntityManagerFactory getEntityManagerFactory() {
return entityManagerFactory;
}
public EntityManager getEntityManager() {
return this.entityManagerFactory.createEntityManager();
}
public void closeEntityManager(EntityManager entityManager) {
if (entityManager != null && entityManager.isOpen()) {
entityManager.close();
}
}
public EntityTransaction getTransaction(EntityManager entityManager) {
return entityManager.getTransaction();
}
public void rollBackTransaction(EntityTransaction transaction) {
if (transaction != null && transaction.isActive()) {
transaction.rollback();
}
}
public void commitTransaction(EntityTransaction transaction) {
if (transaction != null && transaction.isActive()) {
transaction.commit();
}
}
}
Calling code from Base DAO.
public Object getSingleResult(final String queryString, final String key,
final NamedQueryParameter namedQueryParameter) {
EntityTransaction transaction = null;
EntityManager entityManager = null;
try {
entityManager = this.entityMangerService.getEntityManager();
transaction = entityMangerService.getTransaction(entityManager);
transaction.begin();
final Query query = entityManager.createQuery(queryString);
setQueryParameter(query, namedQueryParameter);
final Object result = query.getSingleResult();
entityMangerService.commitTransaction(transaction);
return result;
} catch (final NoResultException e) {
entityMangerService.rollBackTransaction(transaction);
logger.error("Error" : " + e.getMessage());
return null;
} finally {
entityMangerService.closeEntityManager(entityManager);
}
}
Now Here is the problem when ever line entityManager.createQuery(queryString); execute it throws the exception.
2015-06-05 17:39:46,363 WARN DefaultExceptionHandler:94 - Unhandled exception caught by the Stripes default
exception handler.
java.lang.IllegalArgumentException: org.hibernate.hql.internal.ast.QuerySyntaxException: AuthorizationProperty is
not mapped [SELECT pe.value FROM AuthorizationProperty pe WHERE pe.name=:propertyName AND pe.deleted=0]
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1364)
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1300)
at org.hibernate.ejb.AbstractEntityManagerImpl.createQuery(AbstractEntityManagerImpl.java:294)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerInvocationHandler.invoke
(ExtendedEntityManagerCreator.java:334)
at com.sun.proxy.$Proxy53.createQuery(Unknown Source)
at com.company.authentication.dao.AuthorizationBaseDAO.getSingleResult(AuthorizationBaseDAO.java:40)
at com.company.authentication.dao.PropertyDAOImlp.getPropertyValue(PropertyDAOImlp.java:22)
at com.company.authentication.services.AuthorizationPropertyService.getPropertyValueByName
(AuthorizationPropertyService.java:19)
at com.company.rd.servlet.JspAuthorizationRestFilter.hasAuthorizationCode
(JspAuthorizationRestFilter.java:105)
at com.company.rd.servlet.AbstractAuthorizationRestFilter.isRequestAuthenticated
(AbstractAuthorizationRestFilter.java:120)
at com.company.rd.servlet.JspAuthorizationRestFilter.doFilter(JspAuthorizationRestFilter.java:84)
I have debugged the code and found entityManagerFactory for persistenceUnit "codeAuthorization" is not initialized. Only "org.jbpm.persistence.jpa.local" is available (verified through eclipse debugger) inside this method.
Note: This JAR is working fine in some other application where web application and JAR using same way to initialize entityMangerFactory [through Persistence.createEntityManagerFactory("")].
Please let me know How can I get "codeAuthorization" entiryManagerFactory
You are using Spring then use Spring, currently you are doing a lot of work to work around Spring and dependency injection and managed transaction. Don't use Persistence.createEntityManagerFactory(""). Just inject the EntityManager where you need it using an EntityManager field annotated with #PersistenceContext and specify the name of the one you want.
Also don't manage the transactions, entity manager yourself, spring does that for you. For this use the right PlatformTransactionManager the JpaTransactionManager and not the DatasourceTransactionManager as that won't work in a JPA environment. (At least not to manage your JPA transactions).
Doing this will really simplify your code and your life.
So basically ditch the service that is doing those nasty things and simple do things like this in your dao.
#Repository
public class YourDao {
#PersistenceContext(name="codeAuthorization")
private EntityManager em;
#Transactional
public Object getSingleResult(final String queryString, final String key,
final NamedQueryParameter namedQueryParameter) {
final Query query = em.createQuery(queryString);
setQueryParameter(query, namedQueryParameter);
return query.getSingleResult();
}
}
In your configuration replace the DatasourceTransactionManager with the JpaTransactionManager and add <tx:annotation-driven />. Then clean your code.
Note: The JpaTransactionManager is perfectly capable of managing plain JDBC transactions if you still need those, ideally you would have a single transaction manager.
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).
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.