Help with interesting little pointcut: automatically persist @Entity classes

View: New views
2 Messages — Rating Filter:   Alert me  

Help with interesting little pointcut: automatically persist @Entity classes

by matthewadams12 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Hi all,

Looking for a little design help with a nice little pointcut that I
came up with while exploring the use of persistent interfaces in a JPA
project I'm working on.  I have a requirement to call
em.persist(thing) on certain types, and, in an effort to make that
transparent, I've added after returning advice that executes after
@Entity constructors.

It works nicely, except for one thing:  it's called whenever the
persistence provider instantiates a class as well as when the
application does.  My question becomes, then, how do I invoke the
automatic persistence for only those invocations that come from
non-persistence providers?  I can't just skip doing it when a no-args
constructor is called, because, while many of them are added by
enhancement, some of the entities have bona fide no-arg constructors.

My strategy is pretty straightforward:
* Add <context:spring-configured /> to my application context
* Add spring-aspects.jar to my aspect path
* Create an aspect that introduces @Configurable for every @Entity and
inject the EntityManager into each domain object:

public aspect SpringConfigurableMixin {

        public static interface HasEntityManager {
                EntityManager getEntityManager();
        }
       
        declare @type : (@Entity *) : @Configurable(autowire =
Autowire.BY_TYPE, preConstruction = true);
       
        declare parents : (@Entity *) implements HasEntityManager;
       
        @PersistenceContext
        transient private EntityManager HasEntityManager.em;
       
        public EntityManager HasEntityManager.getEntityManager() {
                return em;
        }
}

* Now that every @Entity has the current thread's EntityManager,
introduce an @Autopersist annotation (or let the user do it -- see
code comment) for every @Entity, then, for every @Entity that is also
annotated with @Autopersist, call persist after construction:

privileged aspect AutopersistAspect {

        private static Logger log = LoggerFactory.getLogger(AutopersistAspect.class);
       
        pointcut autopersistentEntity(SpringConfigurableMixin.HasEntityManager
entity) :
                within(@Entity *)
                && ((within(@Autopersist *)
                                        && execution(*.new(..)))
                        || execution(@Autopersist *.new(..))
                        || execution(@Autopersist * *(..)))
                && target(entity);

        // introduce @Autopersist:  not really necessary, just convenient
        declare @type :
                (@Entity *)
                && (!@Autopersist *)
                : @Autopersist;

        after(SpringConfigurableMixin.HasEntityManager entity) returning :
                autopersistentEntity(entity) {

                EntityManager em = entity.getEntityManager();
                if (em == null) {
                        log.warn("can't automatically persist object of type [" +
entity.getClass() + ": EntityManager is null");
                }
                else if (em.contains(entity)) {
                        log.warn("skipping autopersistence of entity of type [" +
entity.getClass() + "]: entity already persistent via EntityManager ["
+ em + "]");
                }
                else {
                        log.info("automatically persisting object of type [" +
entity.getClass() + " with EntityManager [" + em + "]");
                        em.persist(entity);
                        log.debug("automatically persisted object of type [" +
entity.getClass() + " with EntityManager [" + em + "]");
                }
        }
}


Now this almost does the trick.  Problem is, too many constructor
invocations are advised:  not only the ones called by the object model
client or internally by other object model classes, but also the ones
called by the persistence provider as well, resulting, not
surprisingly, in a StackOverflowError.

My question:  how can I design a way to only advise constructors
**not** called by the persistence provider?  Alternatively, how can I
design a way to only advise constructors called from within the object
model or from object model clients?

Thanks,
Matthew
--
mailto:matthew@...
skype:matthewadams12
yahoo:matthewadams
aol:matthewadams12
google-talk:matthewadams12@...
msn:matthew@...
http://matthewadams.me
http://www.linkedin.com/in/matthewadams
_______________________________________________
aspectj-users mailing list
aspectj-users@...
https://dev.eclipse.org/mailman/listinfo/aspectj-users

Re: Help with interesting little pointcut: automatically persist @Entity classes

by Andrew Eisenberg :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Hi Matthew,

Here is where you can use cflow to good measure:

pointcut insidePersistenceProvider() : cflow(within(PersistenceProvider));

And add   && !insidePersistenceProvider() to your autopersistentEntity pointcut.

Just a warning though, your autopersistentEntity pointcut is looking a
little complex.  I'd try breaking it up or documenting it a bit.  3
months from now, I'm going to guess that you will have no idea what
that pointcut does any more.

--a

> Now this almost does the trick.  Problem is, too many constructor
> invocations are advised:  not only the ones called by the object model
> client or internally by other object model classes, but also the ones
> called by the persistence provider as well, resulting, not
> surprisingly, in a StackOverflowError.
>
> My question:  how can I design a way to only advise constructors
> **not** called by the persistence provider?  Alternatively, how can I
> design a way to only advise constructors called from within the object
> model or from object model clients?
_______________________________________________
aspectj-users mailing list
aspectj-users@...
https://dev.eclipse.org/mailman/listinfo/aspectj-users