Announcement Announcement Module
Collapse
No announcement yet.
Translating Spring exceptions Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Translating Spring exceptions

    Our application uses Spring and Hibernate for the core system. We have a simple 3 tier system: weblayer > service layer > dao layer.

    We've setup the Spring transaction strategy with
    Code:
    <tx:annotation-driven/>
    and that is working fine (in conjunction with the @Transactional annotations). I've also tagged the Dao with the @Repository annotations.

    The core is providing the API for a web front-end application. So what we want to do is translate all exceptions to our own API specific exceptions.

    The problem is that we cannot catch those exceptions in our service layer (transactional layer), because some of the runtime exceptions (e.g. OptimisticLockingException) are thrown after the transaction is being committed.

    Now are there several workarounds for this problem:
    1) use a facade (non-transactional) / delegate layer
    2) use AOP-around advice for exception translations: didn't get it to work properly
    3) use another form of transactions (programmatic?)

    What should I do to provide my own custom exceptions in proper way to the users of this API?

  • #2
    I would have thought AOP is perfect here. All you need to ensure is that your AOP runs before the transaction (and therefore around it). The reference manual has some information on this.
    http://www.springframework.org/docs/...spectj-example

    Comment


    • #3
      Originally posted by karldmoore View Post
      I would have thought AOP is perfect here. All you need to ensure is that your AOP runs before the transaction (and therefore around it). The reference manual has some information on this.
      http://www.springframework.org/docs/...spectj-example
      I've already tried to get it working with AspectJ (and implementing the Ordered interface, resembling the example in chapter9), I did set the order property on the aspect (to 1), but didn't know how I could apply this to my annotation-driver transaction manager. Is it possible to provide me with a working example?

      I rather don't want to use AspectJ (introducing complexity), is there a way I could do it without it?

      Comment


      • #4
        Ok, here's an example that I knocked up for another thread. It does method profiling but it's the same principle.
        Code:
        Profiling
        Hello World!
        java.lang.RuntimeException
        StopWatch 'Profiling': running time (millis) = 110
        -----------------------------------------
        ms     %     Task name
        -----------------------------------------
        00110  100%  execution(doIt)
        applicationContext.xml
        Code:
        <?xml version="1.0" encoding="UTF-8"?> 
        <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        	   xmlns="http://www.springframework.org/schema/beans"
        	   xmlns:aop="http://www.springframework.org/schema/aop"
        	   xmlns:tx="http://www.springframework.org/schema/tx"
        	   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
        	   					   http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
        	     				   http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
             
             <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
                <property name="locations">
                    <list>
                        <value>classpath:configuration.properties</value>
                    </list>
                </property>
            </bean>
            
            <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
                <property name="driverClassName">
                    <value>org.hsqldb.jdbcDriver</value>
                </property>
                <property name="url">
        			<value>jdbc:hsqldb:hsql://localhost</value>
                </property>
                <property name="username">
                    <value>${database.username}</value>
                </property>
                <property name="password">
                    <value>${database.password}</value>
                </property>
            </bean>
            
          	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            	<property name="dataSource" ref="dataSource"/>
          	</bean>
          	
          	<tx:annotation-driven transaction-manager="transactionManager"/>
        
          	<bean id="topLevelBean" class="example.TopLevelBeanImpl"/>
        
        	<bean id="profiler" class="example.MethodProfileInterceptor">
                <property name="order" value="1"/>
            </bean>
            
            <aop:config>
                <aop:aspect id="profilingAspect" ref="profiler">
                    <aop:pointcut id="serviceMethodWithReturnValue" expression="execution(void example.TopLevelBean.*(..))"/>
                    <aop:around method="profile" pointcut-ref="serviceMethodWithReturnValue"/>
                </aop:aspect>
            </aop:config>
          	
        </beans>
        configuration.properties
        Code:
        database.username=sa
        database.password=
        TranactionProfileExample
        Code:
        package example;
        
        import org.springframework.context.ApplicationContext;
        import org.springframework.context.support.ClassPathXmlApplicationContext;
        
        public class TranactionProfileExample
        {
        	public static void main ( String [] args )
        	{
                ApplicationContext context = new ClassPathXmlApplicationContext ( "applicationContext.xml" );
        		TopLevelBean topLevelBean = ( TopLevelBean ) context.getBean ( "topLevelBean" );
        		topLevelBean.doIt ();
        	}
        }
        TopLevelBean
        Code:
        package example;
        
        public interface TopLevelBean
        {
        	void doIt ();
        }
        TopLevelBeanImpl
        Code:
        package example;
        
        import org.springframework.transaction.annotation.Propagation;
        import org.springframework.transaction.annotation.Transactional;
        
        @Transactional (propagation = Propagation.REQUIRED)
        public class TopLevelBeanImpl implements TopLevelBean
        {
        	public void doIt ()
        	{
        		System.out.println ( "Hello World!" );
        		throw new RuntimeException();
        	}
        }
        MethodProfileInterceptor
        Code:
        package example;
        
        import org.aspectj.lang.ProceedingJoinPoint;
        import org.springframework.core.Ordered;
        import org.springframework.util.StopWatch;
        
        public final class MethodProfileInterceptor implements Ordered
        {
        	private int order;
        
        	public int getOrder ()
        	{
        		return order;
        	}
        
        	public void setOrder ( int order )
        	{
        		this.order = order;
        	}
        
        	public Object profile ( ProceedingJoinPoint call ) throws Throwable
        	{
        		System.out.println ( "Profiling" );
        		StopWatch clock = new StopWatch ( "Profiling" );
        		try
        		{
        			clock.start ( call.toShortString () );
        			return call.proceed ();
        		}
        		catch ( Throwable e ) 
        		{
        			System.out.println ( e );
        			throw e;
        		}
        		finally
        		{
        			clock.stop ();
        			System.out.println ( clock.prettyPrint () );
        		}
        	}
        }

        Comment


        • #5
          Hi,

          This is the example that I've already tried; but my transactions are still around my 'profiling' system AND this is a solution using AspectJ.

          Couldn't I use a solution with just SpringAOP or another workaround?

          Comment


          • #6
            Well, I've just run it and looking at the stack it appears to me that the Profiling interceptor runs first and then the TransactionInterceptor. If you want to do the same thing without @Transactional it's pretty straight forward. As for the AOP style, this is simply the Schema-based AOP support. Which version of Spring are you using?

            applicationContext.xml
            Code:
            <?xml version="1.0" encoding="UTF-8"?> 
            <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            	   xmlns="http://www.springframework.org/schema/beans"
            	   xmlns:aop="http://www.springframework.org/schema/aop"
            	   xmlns:tx="http://www.springframework.org/schema/tx"
            	   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
            	   					   http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
            	     				   http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
                 
                 <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
                    <property name="locations">
                        <list>
                            <value>classpath:configuration.properties</value>
                        </list>
                    </property>
                </bean>
                
                <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
                    <property name="driverClassName">
                        <value>org.hsqldb.jdbcDriver</value>
                    </property>
                    <property name="url">
            			<value>jdbc:hsqldb:hsql://localhost</value>
                    </property>
                    <property name="username">
                        <value>${database.username}</value>
                    </property>
                    <property name="password">
                        <value>${database.password}</value>
                    </property>
                </bean>
                
              	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
                	<property name="dataSource" ref="dataSource"/>
              	</bean>
              	
              	<bean id="topLevelBean" class="example.TopLevelBeanImpl"/>
            
            	<tx:advice id="txAdvice" transaction-manager="transactionManager">
                	<tx:attributes>
                  		<tx:method name="*"/>
                	</tx:attributes>
              	</tx:advice>
            
            	<bean id="profiler" class="example.MethodProfileInterceptor">
                    <property name="order" value="1"/>
                </bean>
                
                <aop:config>
            		<aop:pointcut id="topLevelBeanPointcut" expression="execution(void example.TopLevelBean.*(..))"/>
            		<aop:advisor advice-ref="txAdvice" pointcut-ref="topLevelBeanPointcut" order="2"/>
              
                    <aop:aspect id="profilingAspect" ref="profiler">
                        <aop:around method="profile" pointcut-ref="topLevelBeanPointcut"/>
                    </aop:aspect>
                </aop:config>
              	
            </beans>

            Comment


            • #7
              Originally posted by karldmoore View Post
              Well, I've just run it and looking at the stack it appears to me that the Profiling interceptor runs first and then the TransactionInterceptor. If you want to do the same thing without @Transactional it's pretty straight forward. As for the AOP style, this is simply the Schema-based AOP support. Which version of Spring are you using?

              applicationContext.xml
              Code:
              <?xml version="1.0" encoding="UTF-8"?> 
              <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              	   xmlns="http://www.springframework.org/schema/beans"
              	   xmlns:aop="http://www.springframework.org/schema/aop"
              	   xmlns:tx="http://www.springframework.org/schema/tx"
              	   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
              	   					   http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
              	     				   http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
                   
                   <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
                      <property name="locations">
                          <list>
                              <value>classpath:configuration.properties</value>
                          </list>
                      </property>
                  </bean>
                  
                  <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
                      <property name="driverClassName">
                          <value>org.hsqldb.jdbcDriver</value>
                      </property>
                      <property name="url">
              			<value>jdbc:hsqldb:hsql://localhost</value>
                      </property>
                      <property name="username">
                          <value>${database.username}</value>
                      </property>
                      <property name="password">
                          <value>${database.password}</value>
                      </property>
                  </bean>
                  
                	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
                  	<property name="dataSource" ref="dataSource"/>
                	</bean>
                	
                	<bean id="topLevelBean" class="example.TopLevelBeanImpl"/>
              
              	<tx:advice id="txAdvice" transaction-manager="transactionManager">
                  	<tx:attributes>
                    		<tx:method name="*"/>
                  	</tx:attributes>
                	</tx:advice>
              
              	<bean id="profiler" class="example.MethodProfileInterceptor">
                      <property name="order" value="1"/>
                  </bean>
                  
                  <aop:config>
              		<aop:pointcut id="topLevelBeanPointcut" expression="execution(void example.TopLevelBean.*(..))"/>
              		<aop:advisor advice-ref="txAdvice" pointcut-ref="topLevelBeanPointcut" order="2"/>
                
                      <aop:aspect id="profilingAspect" ref="profiler">
                          <aop:around method="profile" pointcut-ref="topLevelBeanPointcut"/>
                      </aop:aspect>
                  </aop:config>
                	
              </beans>
              I'm using the latest version (2.0.3) of the Spring framework. You've defined the order property on the profiler bean (the interceptor), as 1. But how should I interpret 1? There isn't an order property on the transactionManager?

              Comment


              • #8
                The order is specified on the txAdvice.
                Code:
                <aop:advisor advice-ref="txAdvice" pointcut-ref="topLevelBeanPointcut" order="2"/>

                Comment


                • #9
                  Thanks, this seems to work.

                  Are there any other viable solutions I need to consider?

                  Comment


                  • #10
                    Other alternatives....... what more do you want the code works . What are you looking for here? You could use interceptors instead of AOP.

                    Comment


                    • #11
                      I'm looking for the most suitable solution for our system.

                      How should this work with an interceptor approach?

                      I've also been thinking to use a 'business delegate' pattern, for translating these exceptions client-side, as we did in the past with the EJB systems.

                      Which approach do you find the 'cleanest'?

                      Comment


                      • #12
                        IMHO, AOP is probably the 'cleanest' way of doing this. I've used this a few times and I thought it felt like the best solution. There's very little to think about here, it's all done for you. As long as the AOP matches your beans, your working is done. If you are wanting to go with a pre Spring 2.0 approach you could still go this way but use something like BeanNameAutoProxyCreator or ProxyFactoryBean instead. It's the same principle.

                        Comment

                        Working...
                        X