Announcement Announcement Module
Collapse
No announcement yet.
Only one Pointcut woven with @Aspect Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Only one Pointcut woven with @Aspect

    Hello,

    I'm quite new to using AOP (period) and I've been trying to setup a security aspect that automatically saves security information such as entry timestamp, update timestamp, user and user's location by intercepting calls to a BeanDao interface (and subclasses thereof). When calls to BeanDao.save or BeanDao.update are made, the aspect intercepts the call and checks to see if the the bean being saved or updated implements SecurityEventAware (one of my own interfaces), and if so, sets the appropriate security data on it.

    I've managed to get this working fine for the save call, but it won't work for the update call I'm making. I'm using Hibernate implementations for the BeanDao impls. Here's what I've managed to get going so far :

    @Aspect
    public class SecurityAspect
    {
    @Pointcut("execution(* com.mypackage.BeanDao.save(*))")
    public void saveSingleBean() {}

    @Pointcut("execution(* com.mypackage.BeanDao.update(*))")
    public void updateSingleBean() {}
    }


    @Aspect
    public class PersistenceAdvice
    {
    private final Log logger = LogFactory.getLog(getClass());

    @Before("com.mypackage.aspects.SecurityAspect.save SingleBean()")
    public void doSingleSaveSEAAssertion(JoinPoint jp)
    {
    //code that does stuff
    }

    @Before("com.mypackage.aspects.SecurityAspect.upda teSingleBean()")
    public void doSingleUpdateSEAAssertion(JoinPoint jp)
    {
    //code that does stuff
    }
    }

    And here's the problem : when I run a DAO for one of my classes whose corresponding DAO implementation only implements the BeanDao interface, the calls get intercepted and advised just fine. When I run a DAO for another class whose DAO implements a subclass of BeanDao, it doesn't get advised. Can somebody please help me out with this ? I've been googling for this for hours and I'm at the point where I'm just banging my head against the wall. If I can get this to work, it'll keep my code super clean and modular, so I really want to make this work. Any advice ?

    Oh. Just changing the pointcuts for the subinterface hasn't worked either, and I've ensured that I've declared both the aspect and advice classes as beans in my application context along with <aop:aspjectj-autoproxy/>
    Last edited by alexmarshall; Jul 7th, 2007, 12:04 PM. Reason: More relevant info added

  • #2
    I'm not sure I fully understand the problem you have.
    You have a DAO interface with two methods: save() and update() and want to intercept those two method calls.
    When I run a DAO for another class whose DAO implements a subclass of BeanDao, it doesn't get advised.
    I didn't get this part...
    Can you provide more details, please ? Also, it helps a lot if the code is around [ code][ /code] tags.

    Comment


    • #3
      Heh, I suppose that wasn't very clear. I have implementations and interfaces as follows:

      HibernateDaoImplA implements BeanDao

      HibernateDaoImplB extends HibernateBeanDaoImplA implements BeanDaoSubInterface

      BeanDaoSubInterface extends BeanDao

      The advice gets run on HibernateBeanDaoImplA.save(), and HibernateBeanDaoImplB.save(), but not on HibernateBeanDao[A|B].update().

      Also, some new information I've just discovered a couple mins ago : I don't have ASM 2.2 in my classpath (which is apparently required for Spring AOP) but I do have ASM 1.5.3 which is required for Hibernate, and the two are apparently not compatible (I only found this out after trying to upgrade to the latest version of ASM). I figured the difference in versions may have been what was causing the advice not to get applied somehow.

      When I debug the program right before HibernateBeanDao[A|B].update() gets executed and inspect the proxy, the .update() advice and aspect do show up in the proxy's toString() output as being in there. I'm definitely going to continue searching these forums, but at least now I have a new direction to search in. Any ideas ?
      Last edited by alexmarshall; Jul 8th, 2007, 02:58 PM. Reason: Spelling error

      Comment


      • #4
        I don't see why update() shouldn't get advised, as save() is... since the two pointcuts are similar. Can you provide a simple test case ?

        Comment


        • #5
          Hi Andrei,

          Below is a test case I use that involve the two respective DAOs. The application I'm making is a simple time tracker for our employees. (This was supposed to be a quick and simple task where I could learn some new things about spring, maven, and hibernate along the way, but with this roadblock, I'm quickly blowing my time budget).

          public void testPersistence() throws Exception
          {
          Agent agent = (Agent) super.applicationContext.getBean("testAgent");

          this.agentDao.save(agent);

          WorkSession openWorkSession = (WorkSession) super.applicationContext.getBean("testOpenWorkSess ion");

          openWorkSession.getSecurityInfo().setInsertor(agen t);
          openWorkSession.setOwner(agent);

          //save a generic open work session
          this.workSessionDao.save(openWorkSession);

          Collection<WorkSession> sessions = this.workSessionDao.getAll();

          assertNotNull(sessions);
          assertEquals(1, sessions.size());

          WorkSession persisted = sessions.iterator().next();

          assertEquals(openWorkSession.hashCode(), persisted.hashCode());
          assertEquals(openWorkSession, persisted);

          openWorkSession = persisted;

          //add an activity to the work session
          Activity activity = new Activity();

          activity.setStartTime(Calendar.getInstance());
          activity.setEndTime(Calendar.getInstance());
          activity.setOneLineSummary("dog fucking");
          activity.setSecurityInfo(openWorkSession.getSecuri tyInfo());

          openWorkSession.addActivity(activity);

          this.workSessionDao.update(null, openWorkSession);

          sessions = this.workSessionDao.getAll();

          assertNotNull(sessions);
          assertEquals(1, sessions.size());

          persisted = sessions.iterator().next();

          assertEquals(openWorkSession.hashCode(), persisted.hashCode());
          assertEquals(openWorkSession, persisted);

          //remove breaks and activities from the work session.
          openWorkSession = persisted;
          openWorkSession.clearActivities();
          openWorkSession.clearBreakPeriods();

          this.workSessionDao.update(null, openWorkSession);

          sessions = this.workSessionDao.getAll();

          assertNotNull(sessions);
          assertEquals(1, sessions.size());

          persisted = sessions.iterator().next();

          assertTrue(persisted.getActivities() == null || persisted.getActivities().size() == 0);
          assertTrue(persisted.getBreaks() == null || persisted.getBreaks().size() == 0);
          assertEquals(openWorkSession, persisted);
          assertEquals(openWorkSession.hashCode(), persisted.hashCode());
          }

          As previously mentioned, the save() methods get intercepted, but the update() methods do not. If I've misinterpreted what you meant by a simple test case, please let me know and I'll happily post anything else you might want to see.

          Comment


          • #6
            I see your pointcut is defined with one parameter sent to the update() method:
            Code:
            execution(* com.mypackage.BeanDao.update(*))
            But, from your example I see the only update() method call is for a method with two parameters:
            Code:
            this.workSessionDao.update(null, openWorkSession);

            Comment


            • #7
              Hi Andrei,

              You're correct. I typed in * when I should've typed in *,*, but in either case, I still should've gone to another of the break points in my program and seen a slough of debug output as the there are other overridden update() methods in the BeanDao interface.

              Oddly, I did up a similar situation when I went home in my test sandbox, and I was able to get multiple advices on multiple pointcuts on the same interface working, and I don't know what the difference between there and here (work) is.

              Even if I change the pointcut to have (..) for the argument pattern to match any number of arguments, nothing changes.

              *Edit*
              It seems I have to revise some of my previous statements and provide some more information, with specific classes:

              I have the following interfaces :
              BeanDao<E>, WorkSessionDao implements BeanDao<WorkSession>, HibernateBeanDaoImpl<E> implements BeanDao<E>, HibernateWorkSessionDaoImpl extends HibernateBeanDaoImpl<WorkSession> implements WorkSessionDao, HibernateAgentDaoImpl extends HibernateBeanDaoImpl<Agent> implements UserDetailsService (for Acegi)

              And now for the bit that was not accurate before : I had not previously run my test for the HibernateAgentDaoImpl with aspects enabled and just assumed that the update()
              call on that bean would not be intercepted. I was wrong. I just ran the test, and the update() method *DOES* in fact get intercepted, but HibernateWorkSessionDaoImpl.update() *DOES NOT* get intercepted.

              My classes look like the following :
              public class HibernateAgentDaoImpl extends HibernateBeanDaoImpl<Agent> implements UserDetailsService {

              public HibernateAgentDaoImpl(SessionFactory factory) {
              //do stuff
              }

              public UserDetails loadUserByUsername(final String username) throws UsernameNotFoundException, DataAccessException {
              //do stuff
              }

              @Override //overrides HibernateBeanDaoImpl.deleteAll
              public void deleteAll() throws Exception {
              //do stuff
              }

              }

              public class HibernateWorkSessionDaoImpl extends HibernateBeanDaoImpl<WorkSession> implements WorkSessionDao
              {

              public HibernateWorkSessionDaoImpl(SessionFactory arg0) {
              //do stuff
              }

              @Override // overrides HibernateBeanDaoImpl.update()
              public void update(final WorkSession oldObject, final WorkSession newObject) throws Exception {
              //do stuff
              }

              @Override //overrides HibernateBeanDaoImpl.deleteAll
              public void deleteAll() throws Exception {
              //do stuff
              }

              //implements a WorkSessionDao method
              public List<WorkSession> findOpenSessionsByOwner(final Agent owner) throws Exception {
              //do stuff
              }

              //implements a WorkSessionDao method
              public List<WorkSession> findRecentlyClosedSessionsByOwner(final Agent owner, final int days) throws Exception {
              //do stuff
              }
              }

              The difference between the HibernateWorkSessionDaoImpl and HibernateAgentDaoImpl is that update() is overridden in HWSDI whereas
              it is not in HADI, and when I got this working in my sandbox at home, the calls were all done on exactly the same class (with no subclasses
              or anything overridden).

              After reading some more of the details on join points and the AspectJ syntax (ie "execution" vs "call") located on the AspjectJ website,
              I'm inclined to believe that despite the fact that HibernateWorkSessionDaoImpl overrides update, it should still be intercepted.
              When I comment out the overridden update() method, HibernateWorkSessionDaoImpl gets intercepted properly, but then
              I don't get the proper hibernate behaviour that I need. (Admittedly, I'm not nearly as familiar with Hibernate as I'd like to be,
              so in theory I shouldn't even have to override the update() method in the first place, but there you have it.)

              Any ideas ?
              Last edited by alexmarshall; Jul 9th, 2007, 01:17 PM. Reason: Provide more detailed information

              Comment


              • #8
                After reading more on Join Point signatures in the AspectJ documentation, I'm still convinced that my join point signatures are correct, but even still, the following Pointcut should work : "execution(* update(..)) && this(com.mypackage.BeanDao)"
                and yet the advice doesn't get invoked. Could this be a bug in Spring or AspectJ, or am I just missing something ? (I'm perfectly willing to accept the latter)
                Last edited by alexmarshall; Jul 9th, 2007, 02:51 PM. Reason: Scrub sensitive info

                Comment


                • #9
                  One question: does update() method receive a generic parameter ?
                  If it does, there is a bug reported and fixed on JIRA: http://opensource.atlassian.com/proj...rowse/SPR-3556

                  Comment


                  • #10
                    Yes. Yes it does. Every overloaded version of the update function contains parameters that are generics in one way or another. I'm happy to find out that I'm not going crazy and sad to find this is in fact a bug. But if it's just a bug, I can live with that. In the meantime, I've just switched to using "this(com.mypackage.BeanDao)" as the pointcut and checking parameter names and argument counts in the mean time. Thank you so much for your help Andrei. You've no idea how much I appreciate it
                    Last edited by alexmarshall; Jul 9th, 2007, 04:17 PM.

                    Comment


                    • #11
                      You can take the last version, which contains the actual fix and test again. I didn't make the test again after reporting it.

                      Comment

                      Working...
                      X