Announcement Announcement Module
Collapse
No announcement yet.
AOP Proxy doesn't intercept equals or hashCode methods Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • AOP Proxy doesn't intercept equals or hashCode methods

    I'm working with using an AOP logger for a project that I'm working on and have had success for the most part. All of the methods in my classes are intercepted and logged except calls to equals and hashCode.

    Here are some of the files that I've got...

    Sample Class
    Code:
    package info.cshp.lomboktest;
    
    public class TestLombok
    {
        private String  name;
        private Integer value;
    
        public TestLombok()
        {
            name = "";
            value = -1;
        }
    
        public TestLombok(String name, Integer value)
        {
            this.name = name;
            this.value = value;
        }
    
        public void testMethod(TestLombok other)
        {
            if (getName() == other.getName())
            {}
        }
    
        @Override
        public boolean equals(Object other)
        {
            boolean returnValue = false;
    
            if ((null != other) && (getClass() == other.getClass()))
            {
                TestLombok o = (TestLombok) other;
                if (name.equals(o.name) && value.equals(o.value))
                {
                    returnValue = true;
                }
            }
    
            return returnValue;
        }
    
        @Override
        public int hashCode()
        {
            return -1;
        }
    
        public String getName()
        {
            return name;
        }
    
        public Integer getValue()
        {
            return value;
        }
    
        public void setName(String name)
        {
            this.name = name;
        }
    
        public void setValue(int value)
        {
            this.value = value;
        }
    }
    JUnit Test Class
    Code:
    package info.cshp.lomboktest;
    
    import static org.junit.Assert.assertEquals;
    import static org.junit.Assert.assertFalse;
    import static org.junit.Assert.assertTrue;
    
    import org.junit.Test;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class TestTestLombok
    {
        @Test
        public void firstTest()
        {
            ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
    
            TestLombok testObject1 = (TestLombok) context.getBean("testBean", new Object[] { "testBean1", 1 });
            TestLombok testObject2 = (TestLombok) context.getBean("testBean", new Object[] { "testBean2", 2 });
            TestLombok testObject4 = (TestLombok) context.getBean("testBean", new Object[] { "testBean1", 1 });
    
            // Create an object that should be identical to the first object.
            TestLombok testObject3 = new TestLombok("testBean1", 1);
            TestLombok testObject5 = new TestLombok("testBean1", 1);
    
            testObject2.getName();
    
            testObject1.toString();
            assertTrue("Objects should equal themselves.", testObject3.equals(testObject3));
            assertTrue("Objects should equal themselves.", testObject1.equals(testObject1));
            assertEquals("Ojbects hashCodes should be equal to same object.", testObject1.hashCode(), testObject3.hashCode());
            assertFalse("Objects 1 & 2 should not be equal to each other.", testObject1.equals(testObject2));
        }
    }
    bean defs (beans.xml)
    Code:
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xmlns:aop="http://www.springframework.org/schema/aop"
    	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
    		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
    
    	<bean id="aopLogger" class="info.cshp.lomboktest.LoggingAspect" />
    	<aop:config proxy-target-class="true">
    		<aop:aspect ref="aopLogger">
    			<aop:pointcut id="loggerPointcut"
    				expression="execution(* info.cshp.lomboktest..*.*(..))" />
    			<aop:around pointcut-ref="loggerPointcut" method="log" />
    		</aop:aspect>
    	</aop:config>
    	
    	<bean id="testBean" class="info.cshp.lomboktest.TestLombok" scope="prototype">
    	   <constructor-arg index="0"><null/></constructor-arg>
    	   <constructor-arg index="1"><null/></constructor-arg>
    	</bean>
    </beans>
    AOP Logger
    Code:
    package info.cshp.lomboktest;
    
    
    import org.apache.log4j.Logger;
    import org.aspectj.lang.ProceedingJoinPoint;
    
    public class LoggingAspect
    {
    
        /**
         * Performs trace level logging going into and out of a method.
         * 
         * @param call
         * @return
         * @throws Throwable
         */
        public Object log(ProceedingJoinPoint call) throws Throwable
        {
            Logger logger = Logger.getLogger(call.getTarget().getClass());
            String methodName = call.toShortString().replace("execution(", "").replace(")", "");
            String args = "";
    
            if (call.getArgs().length > 0)
            {
                args = call.getArgs().toString();
            }
    
            // Log entering method
            logger.trace(String.format("--> %s(%s)", methodName, args));
    
            // Perform call
            Object returnValue = call.proceed();
    
            // Log exit method
            logger.trace(String.format("<-- %s(%s) returns %s", methodName, args, returnValue));
    
            return returnValue;
        }
    }
    log4j.properties
    Code:
    # Set root logger level to DEBUG and its only appender to Default.
    log4j.rootLogger=TRACE, Default
    
    # A1 is set to be a RollingFileAppender
    log4j.appender.Default=org.apache.log4j.RollingFileAppender
    
    log4j.appender.Default.File=lomboktest.log
    log4j.appender.Default.MaxBackupIndex=0
    log4j.appender.Default.layout=org.apache.log4j.PatternLayout
    log4j.appender.Default.layout.ConversionPattern=%d [%t] %-5p %c - %m%n
    
    log4j.logger.org.springframework=ERROR, Default


    For the execution of the test, all that ever appears in the logs is this (currently the test fails at the hashCode test, but it shouldn't.... this is another issue in that the proxied ones get the same hashcodes for equal objects, but non-proxied ones get different hashcodes).

    Code:
    2009-08-19 12:34:17,014 [main] TRACE info.cshp.lomboktest.TestLombok - --> getName()
    2009-08-19 12:34:17,015 [main] TRACE info.cshp.lomboktest.TestLombok - <-- getName() returns testBean2
    Any reasons as to why it isn't intercepting the calls to equals or hashCode?

    One thing to note... when I run this in a debugger and place a breakpoint on the first assert.... I get the following results...

    testObject1.hashCode() = -1922501337
    testObject2.hashCode() = -1922501337
    testObject3.hashCode() = -1
    testObject4.hashCode() = -1922501337
    testObject5.hashCode() = -1

    It looks like the proxy is doing something bizzare with hashCode. Is it possible that the proxy object is not using the correct hashCode and equals methods in the class it is wrapping?
    Last edited by davija; Aug 19th, 2009, 02:59 PM.

  • #2
    Ok, after doing a little bit more poking around, it appears that the proxy objects override equals and hashcode methods. This is what was leading to my issues.

    I solved them by switching to aspectJ and everything works fine.

    Comment


    • #3
      Can you please share how did you do it?

      Comment


      • #4
        Originally posted by ryester View Post
        Can you please share how did you do it?
        He is talking about aspectj weaving. Feel free to read more on that here - Weaving with AspectJ

        Comment


        • #5
          Hi,

          this can be done without using AspectJ, but with the Spring AOP.

          Please see the solution posted here:

          http://forum.springsource.org/showthread.php?t=14767

          Thanks,
          Danny

          Comment

          Working...
          X