Announcement Announcement Module
Collapse
No announcement yet.
Using populateProtectedVariables in Unit Tests Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Using populateProtectedVariables in Unit Tests

    Hi,

    I'm trying to set populateProtectedVariables to true for org.springframework.test.AbstractTransactionalData SourceSpringContextTests. This is because I've run into a problem where the default setter injection by type does not work as I have a DAO wrapped in a Transaction Manager and Spring fails because it cannot choose between the original DAO and mgr that wraps it (The both implement the DAO interface).

    Anyway, when I set the fields to protected and call populateProtectedVariables( true ) in my unit test constructor it fails with the following error

    java.lang.IllegalAccessException: Class org.springframework.test.AbstractDependencyInjecti onSpringContextTests can not access a member of class com.product.dao.CampusTest with modifiers "protected"

    Any else had this problem, got any tips ?

    thanks, matt.

  • #2
    Matt,

    Can you provide a stack trace? This should work. Now inside some appserver environments with security set a certain way untrusted code might not be allowed to access protected fields via reflection, but from an ide and the like there should be no problem.

    Comment


    • #3
      Colin,

      apologies for the delay, was sick yesterday. Ok, so here's the setup. I'm running this in a JUnit test which is being run through the JUnit plugin of Eclipse 3.0.1. I'm using JDK 1.4.2_04. I can't imagine it is affects this but I'm running XP SP2 with 1Gb of RAM, 2.8 GHz P4.

      here's the unit test itself

      Code:
      import java.util.List;
      
      import org.apache.commons.logging.Log;
      import org.apache.commons.logging.LogFactory;
      import org.springframework.context.support.ClassPathXmlApplicationContext;
      import org.springframework.test.AbstractTransactionalDataSourceSpringContextTests;
      
      public class CopyOfCampusDAOTest extends AbstractTransactionalDataSourceSpringContextTests
      {
          protected CampusDAO campusDAO;
          
          protected SequGeneratorDAO sequGeneratorDAO;
          
          private static final Log log = LogFactory.getLog(CopyOfCampusDAOTest.class);
          
          private Campus campus;    
      
          public CopyOfCampusDAOTest()
          {
              super();
              
              setPopulateProtectedVariables(true);
          }
          
          /**
           * @see org.springframework.test.AbstractDependencyInjectionSpringContextTests#getConfigLocations()
           */
          protected String[] getConfigLocations()
          {
              return new String[] { "spring.xml" };
          }
          
          /**
           * @see org.springframework.test.AbstractTransactionalSpringContextTests#onSetUpInTransaction()
           */
          protected void onSetUpInTransaction()
                  throws Exception
          {        
              int campusSequ = sequGeneratorDAO.getNextSequ("F_CAMPUS");
              
              campus = new Campus();
              campus.setSequ(campusSequ);
              campus.setCode("TEST");
              campus.setName("TEST CAMPUS");
              
              campusDAO.create(campus);
          }
          
          public void testFindAll()
          {
              List all = campusDAO.findAll();
              
              log.debug(all);
              
              assertTrue("Test campus not found", all.contains(campus));
          }
          
          public void testFindByID()
          {
              Campus test = campusDAO.getCampusBySequ(campus.getSequ());
              
              assertTrue("ID does not match", test.getSequ() == campus.getSequ());
              assertTrue("Code does not match", test.getCode().equals(campus.getCode()));
          }
      }
      Campus is a simple bean. SequGeneratorDAO is a DAO that gets the next sequ (ID, sequence number ...) for a given table. Here is the relevant parts of the spring config

      Code:
      <?xml version="1.0" encoding="UTF-8"?>
      <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http&#58;//www.springframework.org/dtd/spring-beans.dtd">
      <beans default-autowire="no" default-dependency-check="none" default-lazy-init="false">
          <description>Spring Framework configuration file for the application.</description>
          
          
          <!-- ================================================== -->
      	<!-- Transaction Manager                                -->
      	<!-- ================================================== -->  
      	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
      		<property name="dataSource">
                      <ref local="edisDataSource"/>
              </property>
      	</bean>
          
          <!-- ================================================== -->
          <!-- iBATIS SQL Maps 2.0 config                         -->
          <!-- ================================================== -->
          <bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
              <property name="configLocation">
                  <value>classpath&#58;ibatis.xml</value>
              </property>
          </bean>
          
          
          <bean id="campusDAO" class="com.hass.product.dao.ibatis.CampusDAOImpl">
              <property name="dataSource">
                  <ref local="edisDataSource"/>
              </property>
              <property name="sqlMapClient">
                  <ref local="sqlMapClient"/>
              </property>
          </bean>
          
          <bean id="sequGeneratorDAOTarget" class="com.hass.product.dao.ibatis.SequGeneratorDAOImpl">
              <property name="dataSource">
                  <ref local="edisDataSource"/>
              </property>
              <property name="sqlMapClient">
                  <ref local="sqlMapClient"/>
              </property>
          </bean>
          
          <!-- ================================================== -->
      	<!-- Transaction Proxies                                -->
      	<!-- ================================================== --> 
          
      	<bean id="sequGeneratorDAO" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
      		<property name="transactionManager"><ref bean="transactionManager"/></property>
      		<property name="target"><ref bean="sequGeneratorDAOTarget"/></property>
      		<property name="transactionAttributes">
      			<props>
      				<prop key="findNextSequ*">PROPAGATION_REQUIRED</prop>
      			</props>
      		</property>
      	</bean> 
          
          
      </beans>
      so the sequGeneratorDAO bean is wrapped by a transaction proxy. This is the reason why I don't used injection by type. Last of all, here's the stack trace (the bit you really wanted :wink: )

      Code:
      java.lang.IllegalAccessException&#58; Class org.springframework.test.AbstractDependencyInjectionSpringContextTests can not access a member of class com.hass.product.dao.CopyOfCampusDAOTest with modifiers "protected"
        at sun.reflect.Reflection.ensureMemberAccess&#40;Reflection.java&#58;57&#41;
        at java.lang.reflect.Field.doSecurityCheck&#40;Field.java&#58;811&#41;
        at java.lang.reflect.Field.getFieldAccessor&#40;Field.java&#58;758&#41;
        at java.lang.reflect.Field.set&#40;Field.java&#58;519&#41;
        at org.springframework.test.AbstractDependencyInjectionSpringContextTests.populateProtectedVariables&#40;AbstractDependencyInjectionSpringContextTests.java&#58;184&#41;
        at org.springframework.test.AbstractDependencyInjectionSpringContextTests.setUp&#40;AbstractDependencyInjectionSpringContextTests.java&#58;115&#41;
        at junit.framework.TestCase.runBare&#40;TestCase.java&#58;125&#41;
        at junit.framework.TestResult$1.protect&#40;TestResult.java&#58;106&#41;
        at junit.framework.TestResult.runProtected&#40;TestResult.java&#58;124&#41;
        at junit.framework.TestResult.run&#40;TestResult.java&#58;109&#41;
        at junit.framework.TestCase.run&#40;TestCase.java&#58;118&#41;
        at junit.framework.TestSuite.runTest&#40;TestSuite.java&#58;208&#41;
        at junit.framework.TestSuite.run&#40;TestSuite.java&#58;203&#41;
        at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests&#40;RemoteTestRunner.java&#58;421&#41;
        at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run&#40;RemoteTestRunner.java&#58;305&#41;
        at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main&#40;RemoteTestRunner.java&#58;186&#41;
      I did try with setter methods for the variables. This didn't seem to make any difference.

      In the end I got round it by using ClassPathXmlApplicationContext to get the config and then the DAO. I'm happy to help you find out why the populateProtectedVariables flag didn't work though.

      matt.

      Comment


      • #4
        Any chance you can try the same thing from command-line? If it works, then it's probably Eclipse setting a pretty restrictive reflection access policy. We can at least document that in the JavaDoc...

        Comment


        • #5
          tried to run it through ant on the command line, same error -

          Code:
          Testsuite&#58; com.hass.product.edisweb.dao.CopyOfCampusDAOTest
          Tests run&#58; 2, Failures&#58; 0, Errors&#58; 2, Time elapsed&#58; 1.359 sec
          
          Testcase&#58; testFindAll took 1.125 sec
          	Caused an ERROR
          Class org.springframework.test.AbstractDependencyInjectionSpringContextTests can not access a member of class com.hass.product.edisweb.dao.CopyOfCampusDAOTest with modifiers "protected"
          java.lang.IllegalAccessException&#58; Class org.springframework.test.AbstractDependencyInjectionSpringContextTests can not access a member of class com.hass.product.edisweb.dao.CopyOfCampusDAOTest with modifiers "protected"
          	at sun.reflect.Reflection.ensureMemberAccess&#40;Reflection.java&#58;57&#41;
          	at java.lang.reflect.Field.doSecurityCheck&#40;Field.java&#58;811&#41;
          	at java.lang.reflect.Field.getFieldAccessor&#40;Field.java&#58;758&#41;
          	at java.lang.reflect.Field.set&#40;Field.java&#58;519&#41;
          	at org.springframework.test.AbstractDependencyInjectionSpringContextTests.populateProtectedVariables&#40;AbstractDependencyInjectionSpringContextTests.java&#58;184&#41;
          	at org.springframework.test.AbstractDependencyInjectionSpringContextTests.setUp&#40;AbstractDependencyInjectionSpringContextTests.java&#58;115&#41;
          
          Testcase&#58; testFindByID took 0.234 sec
          	Caused an ERROR
          Class org.springframework.test.AbstractDependencyInjectionSpringContextTests can not access a member of class com.hass.product.edisweb.dao.CopyOfCampusDAOTest with modifiers "protected"
          java.lang.IllegalAccessException&#58; Class org.springframework.test.AbstractDependencyInjectionSpringContextTests can not access a member of class com.hass.product.edisweb.dao.CopyOfCampusDAOTest with modifiers "protected"
          	at sun.reflect.Reflection.ensureMemberAccess&#40;Reflection.java&#58;57&#41;
          	at java.lang.reflect.Field.doSecurityCheck&#40;Field.java&#58;811&#41;
          	at java.lang.reflect.Field.getFieldAccessor&#40;Field.java&#58;758&#41;
          	at java.lang.reflect.Field.set&#40;Field.java&#58;519&#41;
          	at org.springframework.test.AbstractDependencyInjectionSpringContextTests.populateProtectedVariables&#40;AbstractDependencyInjectionSpringContextTests.java&#58;184&#41;
          	at org.springframework.test.AbstractDependencyInjectionSpringContextTests.setUp&#40;AbstractDependencyInjectionSpringContextTests.java&#58;115&#41;
          this is with ant 1.6.1 and junit 3.8.1, all else same as before. I get the same error if I run this build script in Eclipse' Ant plugin as well, which is using ant 1.6.2.

          Comment


          • #6
            I found exactly the same problem when trying to enable autowiring by name.
            My context and testcases are similar to yours.

            The problem arises in method populateProtectedVariables(), when trying to set the value of a managed (protected) field. I encountered it under Eclipse 3.0.1 and on the command line with Ant 1.6.2. JDK version: 1.4.2_06.

            For the moment I have overridden initManagedVariableNames() in my testcase superclass to manage only public variables. This works OK.

            Is it some kind of bug or a platform limitation ?
            Baptiste

            Comment


            • #7
              I am having the same problem running my tests in JBoss 4.0.1 under jdk 1.5.0_01.

              Should I file a JIRA bug report on this?

              Comment


              • #8
                Baptiste and cepage, are you using the current spring 1.1.4, or an older version. V1.1.4 added a call to Field.setAccessible(true) on the specified field, which should ensure that no more IllegalAccessException is thrown, unless the current SecurityManager doesn't allow setting the setAccessible flag to true, in which case that call itself should throw a security exception...

                Comment


                • #9
                  Thanks, Colin. I am running on 1.1.3. I will upgrade and retry.

                  Corby

                  Comment

                  Working...
                  X