[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
[aspectj-users] Help with interesting little pointcut: automatically persist @Entity classes

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@xxxxxxxxxxxxxxx
skype:matthewadams12
yahoo:matthewadams
aol:matthewadams12
google-talk:matthewadams12@xxxxxxxxx
msn:matthew@xxxxxxxxxxxxxxx
http://matthewadams.me
http://www.linkedin.com/in/matthewadams