Unable to get SchemaAction.CREATE to work - spring-data-cassandra

My Spring Data Cassandra configuration looks like this:
#Configuration
#EnableCassandraRepositories(basePackages = {
"mypackage.repository.cassandra",
})
public class DistributedRepositoryConfiguration {
// ...
#Bean
public CassandraSessionFactoryBean session() throws Exception {
CassandraSessionFactoryBean session = new CassandraSessionFactoryBean();
session.setCluster(cluster().getObject());
session.setKeyspaceName(configuration.get().getKeyspace());
session.setConverter(converter());
session.setSchemaAction(SchemaAction.CREATE);
return session;
}
}
Generally, Spring Data Cassandra works in my project. However, when I start my application I have no tables created. Anyone who could tell me what I'm doing wrong?

It is not written well in documentation if you want to have automatic table creation you should tell cassandra where to look for entity classes:
<cassandra:mapping entity-base-packages="your.package" />
If you want to do the same using annotiation configuration you have to explicitly tell CassandraTemplate where to look for it. So
#Bean
public CassandraSessionFactoryBean session() throws Exception {
CassandraSessionFactoryBean session = new CassandraSessionFactoryBean();
session.setCluster(cluster().getObject());
session.setKeyspaceName(keyspaceName);
session.setConverter(converter());
session.setSchemaAction(SchemaAction.CREATE);
return session;
}
#Bean
public CassandraConverter converter() throws Exception {
return new MappingCassandraConverter(mappingContext());
}
#Bean
public CassandraMappingContext mappingContext() throws Exception {
BasicCassandraMappingContext bean = new BasicCassandraMappingContext();
bean.setInitialEntitySet(CassandraEntityClassScanner.scan(("package.with.your.entities")));
return bean;
}
To do it with ease I suggest using AbstractCassandraConfiguration and override methods which You need.

I checked the class AbstractCassandraConfiguration and found the following code:
public String[] getEntityBasePackages() {
return new String[] { getClass().getPackage().getName() };
}
Since my config class isn't in the main package, the component scan does not find my classes with the #Table annotation. So I override the method "getEntityBasePackages()" using my StartUp class and everything worked fine.
This is my config class:
#Configuration
public class CassandraConfig extends AbstractCassandraConfiguration {
#Value("${spring.data.cassandra.keyspace-name}")
private String keyspaceName;
#Override
protected String getKeyspaceName() {
return keyspaceName;
}
#Override
public String[] getEntityBasePackages() {
return new String[]{AppStartup.class.getPackage().getName()};
}
#Override
protected List<CreateKeyspaceSpecification> getKeyspaceCreations() {
return Collections.singletonList(CreateKeyspaceSpecification
.createKeyspace(keyspaceName)
.ifNotExists(true)
.with(KeyspaceOption.DURABLE_WRITES, true)
.withSimpleReplication());
}
#Override
public SchemaAction getSchemaAction() {
return SchemaAction.CREATE_IF_NOT_EXISTS;
}
}
Using this class, your application should create the required keyspace and tables to run.

If you using CassandraDataConfiguration just annotated you base class application with:
#EntityScan("mypackage.repository.cassandra")
The base package information will be used in CassandraDataAutoConfiguration.cassandraMapping method to add this package to cassandra mapping.

Related

Connect to 2 cassandra clusters in spring data

I am trying to connect to 2 different cassandra clusters using spring data cassandra. But it always uses only the first cassandra cluster config. The second one is not taking affect. Any idea what am I doing wrong? This is the config that I am using:
First cassandra cluster config:
#Configuration
#EnableCassandraRepositories(
basePackageClasses = SourceRepository.class
)
public class SourceCassandraConfig extends AbstractCassandraConfiguration {
#Override
public String getContactPoints() {
return "localhost";
}
#Override
public int getPort() {
return "9051";
}
#Override
protected String getKeyspaceName() {
return "source_keyspace";
}
}
Second cassandra cluster config:
#Configuration
#EnableCassandraRepositories(
basePackageClasses = TargetRepository.class,
cassandraTemplateRef = "targetCassandraTemplate"
)
public class TargetCassandraConfig extends AbstractCassandraConfiguration {
#Override
public String getContactPoints() {
return "localhost";
}
#Override
public int getPort() {
return "9052";
}
#Override
protected String getKeyspaceName() {
return "target_keyspace";
}
#Override
#Bean("targetSession")
public CassandraSessionFactoryBean session() throws ClassNotFoundException {
final CassandraSessionFactoryBean session = super.session();
session.setKeyspaceName(getKeyspaceName());
session.setCluster(cluster().getObject());
return session;
}
#Override
public CassandraCqlClusterFactoryBean cluster() {
CassandraCqlClusterFactoryBean cluster = super.cluster();
cluster.setContactPoints(contactPoints);
cluster.setPort(port);
return cluster;
}
#Bean("targetCassandraTemplate")
public CassandraAdminOperations cassandraTemplate(
#Qualifier("targetSession") final CassandraSessionFactoryBean session) throws Exception {
return new CassandraAdminTemplate(session.getObject(), cassandraConverter());
}
}
I always see that only the first cluster node is getting added
com.datastax.driver.core.Cluster : New Cassandra host localhost/127.0.0.1:9051 added
What am I doing wrong?
I spent 2 days debugging this and 10 mins after posting this question, I found the fix :)
I wasn't using the cluster bean that I created properly in the session bean. So I did the following and it worked:
#Override
#Bean("targetCassandraCluster")
public CassandraCqlClusterFactoryBean cluster() {
CassandraCqlClusterFactoryBean cluster = super.cluster();
cluster.setContactPoints(contactPoints);
cluster.setPort(port);
return cluster;
}
#Bean("targetCassandraSession")
public CassandraSessionFactoryBean session(
#Qualifier("targetCassandraCluster") final CassandraCqlClusterFactoryBean cluster
) throws ClassNotFoundException {
final CassandraSessionFactoryBean session = super.session();
session.setKeyspaceName(getKeyspaceName());
session.setCluster(cluster.getObject());
return session;
}
#Bean("targetCassandraTemplate")
public CassandraAdminOperations cassandraTemplate(
#Qualifier("targetCassandraSession") final CassandraSessionFactoryBean session) throws Exception {
return new CassandraAdminTemplate(session.getObject(), cassandraConverter());
}

onApplicationEvent() is never invoked on DelayHandler

I'm using Spring Boot and Spring Integration Java DSL in my #Configuration class. One of the flows is using DelayHandler with MessageStore, by means of .delay(String groupId, String expression, Consumer endpointConfigurer):
#Bean
public IntegrationFlow errorFlow() {
return IntegrationFlows.from(errorChannel())
...
.delay(...)
...
.get();
}
I was hoping to utilize the reschedulePersistedMessages() functionality of DelayHandler, but I found out the onApplicationEvent(ContextRefreshedEvent event) which invokes it is actually never invoked (?)
I'm not sure, but I suspect this is due to the fact DelayHandler is not registered as a Bean, so registerListeners() in AbstractApplicationContext is not able to automatically register DelayHandler (and registration of non-bean listeners via ApplicationEventMulticaster.addApplicationListener(ApplicationListener listener) is not done for DelayHandler.
Currently I'm using a rather ugly workaround of registering my own listener Bean into which I inject the integration flow Bean, and then invoking the onApplicationEvent() manually after locating the DelayHandler:
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
Set<Object> integrationComponents = errorFlow.getIntegrationComponents();
for (Object component : integrationComponents) {
if (component instanceof DelayerEndpointSpec) {
Tuple2<ConsumerEndpointFactoryBean, DelayHandler> tuple2 = ((DelayerEndpointSpec) component).get();
tuple2.getT2().onApplicationEvent(event);
return;
}
}
}
Well, yes. This test-case confirm the issue:
#ContextConfiguration
#RunWith(SpringJUnit4ClassRunner.class)
#DirtiesContext
public class DelayerTests {
private static MessageGroupStore messageGroupStore = new SimpleMessageStore();
private static String GROUP_ID = "testGroup";
#BeforeClass
public static void setup() {
messageGroupStore.addMessageToGroup(GROUP_ID, new GenericMessage<>("foo"));
}
#Autowired
private PollableChannel results;
#Test
public void testDelayRescheduling() {
Message<?> receive = this.results.receive(10000);
assertNotNull(receive);
assertEquals("foo", receive.getPayload());
assertEquals(1, messageGroupStore.getMessageGroupCount());
assertEquals(0, messageGroupStore.getMessageCountForAllMessageGroups());
}
#Configuration
#EnableIntegration
public static class ContextConfiguration {
#Bean
public IntegrationFlow delayFlow() {
return flow ->
flow.delay(GROUP_ID, (String) null,
e -> e.messageStore(messageGroupStore)
.id("delayer"))
.channel(c -> c.queue("results"));
}
}
}
Here we go: https://github.com/spring-projects/spring-integration-java-dsl/issues/59.
As a workaround we can do this in our #Configuration:
#Autowired
private ApplicationEventMulticaster multicaster;
#PostConstruct
public void setup() {
this.multicaster.addApplicationListenerBean("delayer.handler");
}
Pay attention to the beanName to register. This is exactly that .id("delayer") from our flow definition plus the .handler suffix for the DelayHandler bean definition.

How to initialize Cassandra keyspace and tables with Spring boot

I am using Cassandra as a datasource in my Spring boot application and would like to initialize the database before the application starts.
Up to now what I have done is, I have defined a class "CassandraConfiguration" extending "AbstractCassandraConfiguration" class as in the examples which you can see below and I have a repository extending "CassandraRepository". When I create the keyspace and the table myself, the application works fine.
However, I want to create the keyspace and tables automatically while application is starting. In order to do that, I supplied a schema.cql file under resources folder but I could not make that script work.
Does anyone have any idea what can I do to create the keyspace(s) and tables automatically?
Thanks.
Edit: I am using Cassandra 2.0.9, spring-boot 1.3.2.RELEASE and datastax cassandra driver 2.1.6 versions.
CassandraConfiguration.java
#Configuration
#PropertySource(value = { "classpath:cassandra.properties" })
#EnableCassandraRepositories(basePackages = { "bla.bla.bla.repository" })
public class CassandraConfiguration extends AbstractCassandraConfiguration {
#Autowired
private Environment environment;
#Bean
public CassandraClusterFactoryBean cluster() {
CassandraClusterFactoryBean cluster = new CassandraClusterFactoryBean();
cluster.setContactPoints( environment.getProperty( "cassandra.contactpoints" ) );
cluster.setPort( Integer.parseInt( environment.getProperty( "cassandra.port" ) ) );
return cluster;
}
#Bean
public CassandraMappingContext cassandraMapping() throws ClassNotFoundException {
return new BasicCassandraMappingContext();
}
#Bean
public CassandraConverter converter() throws ClassNotFoundException {
return new MappingCassandraConverter(cassandraMapping());
}
#Override
protected String getKeyspaceName() {
return environment.getProperty( "cassandra.keyspace" );
}
#Bean
public CassandraSessionFactoryBean session() throws Exception {
CassandraSessionFactoryBean session = new CassandraSessionFactoryBean();
session.setCluster(cluster().getObject());
session.setKeyspaceName(environment.getProperty("cassandra.keyspace"));
session.setConverter(converter());
session.setSchemaAction(SchemaAction.NONE);
return session;
}
#Override
public SchemaAction getSchemaAction() {
return SchemaAction.RECREATE_DROP_UNUSED;
}
}
If you are still having problems with this, in Spring Boot 2 and SD Cassandra 2.0.3 you can do this straightforward Java configuration and setup everything out of the box.
#Configuration
#EnableCassandraRepositories(basePackages = "com.example.repository")
public class DbConfigAutoStart extends AbstractCassandraConfiguration {
/*
* Provide a contact point to the configuration.
*/
#Override
public String getContactPoints() {
return "exampleContactPointsUrl";
}
/*
* Provide a keyspace name to the configuration.
*/
#Override
public String getKeyspaceName() {
return "exampleKeyspace";
}
/*
* Automatically creates a Keyspace if it doesn't exist
*/
#Override
protected List<CreateKeyspaceSpecification> getKeyspaceCreations() {
CreateKeyspaceSpecification specification = CreateKeyspaceSpecification
.createKeyspace("exampleKeyspace").ifNotExists()
.with(KeyspaceOption.DURABLE_WRITES, true).withSimpleReplication();
return Arrays.asList(specification);
}
/*
* Automatically configure a table if doesn't exist
*/
#Override
public SchemaAction getSchemaAction() {
return SchemaAction.CREATE_IF_NOT_EXISTS;
}
/*
* Get the entity package (where the entity class has the #Table annotation)
*/
#Override
public String[] getEntityBasePackages() {
return new String[] { "com.example.entity" };
}
And you are good to go
Your return type BasicCassandraMappingContext() might be deprecated. Use
#Bean
public CassandraMappingContext mappingContext() throws ClassNotFoundException {
CassandraMappingContext mappingContext= new CassandraMappingContext();
mappingContext.setInitialEntitySet(getInitialEntitySet());
return mappingContext;
}
#Override
public String[] getEntityBasePackages() {
return new String[]{"base-package name of all your entity, annotated
with #Table"};
}
#Override
protected Set<Class<?>> getInitialEntitySet() throws ClassNotFoundException {
return CassandraEntityClassScanner.scan(getEntityBasePackages());
}
Instead of,
#Bean
public CassandraMappingContext cassandraMapping() throws ClassNotFoundException {
return new BasicCassandraMappingContext();
}
also set:
session.setSchemaAction(SchemaAction.RECREATE_DROP_UNUSED);
and Exclude:
#Override
public SchemaAction getSchemaAction() {
return SchemaAction.RECREATE_DROP_UNUSED;
}
get reference here.
I'm working with spring-boot 1.5.10.RELEASE and cassandra 3.0.16 but you can try downscaling the versions. To create the keyspace you can import the keyspacename from you application.yml or application.properties. Using the #Table annotation your tables should be generated automatically provided you have set the entity base package.
#Value("${cassandra.keyspace}")
private String keySpace;
#Override
public String[] getEntityBasePackages() {
return new String[]{"com.example.your.entities"};
}
#Override
protected List<CreateKeyspaceSpecification> getKeyspaceCreations() {
return Arrays.asList(
CreateKeyspaceSpecification.createKeyspace()
.name(keySpace)
.ifNotExists()
);
}
Finally i got it working by adding setKeyspaceCreations(getKeyspaceCreations()) to the CassandraClusterFactoryBean Override and also make sure to enable #ComponentScan.
import com.datastax.driver.core.PlainTextAuthProvider;
import com.datastax.driver.core.policies.ConstantReconnectionPolicy;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.cassandra.config.*;
import org.springframework.data.cassandra.core.cql.keyspace.CreateKeyspaceSpecification;
import org.springframework.data.cassandra.core.cql.keyspace.DropKeyspaceSpecification;
import org.springframework.data.cassandra.core.cql.keyspace.KeyspaceOption;
import org.springframework.data.cassandra.repository.config.EnableReactiveCassandraRepositories;
import java.util.Arrays;
import java.util.List;
#Configuration
#EnableReactiveCassandraRepositories(basePackages = "com.company.domain.data")
public class CassandraConfig extends AbstractReactiveCassandraConfiguration{
#Value("${spring.data.cassandra.contactpoints}") private String contactPoints;
#Value("${spring.data.cassandra.port}") private int port;
#Value("${spring.data.cassandra.keyspace-name}") private String keyspace;
#Value("${spring.data.cassandra.username}") private String userName;
#Value("${spring.data.cassandra.password}") private String password;
#Value("${cassandra.basepackages}") private String basePackages;
#Override protected String getKeyspaceName() {
return keyspace;
}
#Override protected String getContactPoints() {
return contactPoints;
}
#Override protected int getPort() {
return port;
}
#Override public SchemaAction getSchemaAction() {
return SchemaAction.CREATE_IF_NOT_EXISTS;
}
#Override
public String[] getEntityBasePackages() {
return new String[]{"com.company.domain.data"};
}
#Override
public CassandraClusterFactoryBean cluster() {
PlainTextAuthProvider authProvider = new PlainTextAuthProvider(userName, password);
CassandraClusterFactoryBean cluster=new CassandraClusterFactoryBean();
cluster.setJmxReportingEnabled(false);
cluster.setContactPoints(contactPoints);
cluster.setPort(port);
cluster.setAuthProvider(authProvider);
cluster.setKeyspaceCreations(getKeyspaceCreations());
cluster.setReconnectionPolicy(new ConstantReconnectionPolicy(1000));
return cluster;
}
#Override
protected List<CreateKeyspaceSpecification> getKeyspaceCreations() {
CreateKeyspaceSpecification specification = CreateKeyspaceSpecification.createKeyspace(keyspace)
.ifNotExists()
.with(KeyspaceOption.DURABLE_WRITES, true);
return Arrays.asList(specification);
}
#Override
protected List<DropKeyspaceSpecification> getKeyspaceDrops() {
return Arrays.asList(DropKeyspaceSpecification.dropKeyspace(keyspace));
}
}
The previous answers are based on AbstractCassandraConfiguration from spring-data-cassandra. If you use spring-boot then it can auto-configure Cassandra for you and there's no need to extend AbstractCassandraConfiguration. However, even in this case you need to do some work to automatically create the keyspace. I've settled on an auto-configuration added to our company's spring-boot starter, but you can also define it as a regular configuration in your application.
/**
* create the configured keyspace before the first cqlSession is instantiated. This is guaranteed by running this
* autoconfiguration before the spring-boot one.
*/
#ConditionalOnClass(CqlSession.class)
#ConditionalOnProperty(name = "spring.data.cassandra.create-keyspace", havingValue = "true")
#AutoConfigureBefore(CassandraAutoConfiguration.class)
public class CassandraCreateKeyspaceAutoConfiguration {
private static final Logger logger = LoggerFactory.getLogger(CassandraCreateKeyspaceAutoConfiguration.class);
public CassandraCreateKeyspaceAutoConfiguration(CqlSessionBuilder cqlSessionBuilder, CassandraProperties properties) {
// It's OK to mutate cqlSessionBuilder because it has prototype scope.
try (CqlSession session = cqlSessionBuilder.withKeyspace((CqlIdentifier) null).build()) {
logger.info("Creating keyspace {} ...", properties.getKeyspaceName());
session.execute(CreateKeyspaceCqlGenerator.toCql(
CreateKeyspaceSpecification.createKeyspace(properties.getKeyspaceName()).ifNotExists()));
}
}
}
In my case I've also added a configuration property to control the creation, spring.data.cassandra.create-keyspace, you may leave it out if you don't need the flexibility.
Note that spring-boot auto-configuration depends on certain configuration properties, here's what I have in my dev environment:
spring:
data:
cassandra:
keyspace-name: mykeyspace
contact-points: 127.0.0.1
port: 9042
local-datacenter: datacenter1
schema-action: CREATE_IF_NOT_EXISTS
create-keyspace: true
More details: spring-boot and Cassandra

Managing Multiple Cassandra Sessions

I have multiple Cassandra clusters. Each cluster has specific set of contact points. Each cluster has separate set of tables/CF.
In my C* client I am supposed to query both clusters. I am using spring-boot version of the Cassandra. I am trying to use CassandraOperations to do the queries. How do I go about doing this?
#Bean
public CassandraOperations cassandraTemplate(Session sessionA) throws Exception {
return new CassandraTemplate(sessionA);
}
#Bean
public CassandraMappingContext mappingContext() {
return new BasicCassandraMappingContext();
}
#Bean
public CassandraConverter converter() {
return new MappingCassandraConverter(mappingContext());
}
Above is a example whereby I setup cassandraoperations using sessionA, how about doing the same for sessionB?
At any given time base don the query, it can go to either sessionA or sessionB.
Any pointers are appreciated
Thanks
Found a way to do this by creating separate classes for each cluster manager.
#Component
#Primary
public class CassandraTemplateA extends CassandraTemplate{
#Autowired
public CassandraTemplateA(CassandraConverter converter) {
super(sessionA, converter);
}
}
#Component
#Primary
public class CassandraTemplateB extends CassandraTemplate{
#Autowired
public CassandraTemplateB(CassandraConverter converter) {
super(sessionB, converter);
}
}

How to intercept methods of EntityManager with Seam 3?

I'm trying to intercept the method persist and update of javax.persistence.EntityManager in a Seam 3 project.
In a previous version (Seam 2) of the micro-framework I'm trying to make, I did this using an implementation of org.hibernate.Interceptor and declaring it in the persistence.xml.
But I want something more "CDI-like" now we are in a JEE6 environment.
I want that just before entering in a EntityManager.persist call, an event #BeforeTrackablePersist is thrown. The same way, I want an event #BeforeTrackableUpdate to be thrown before entering in a EntityManager.merge call. Trackable is an interface which some of my Entitys could implement in order to be intercepted before persist or merge.
I'm using Seam 3 (3.1.0.Beta3) Extended Persistence Manager :
public class EntityManagerHandler {
#SuppressWarnings("unused")
#ExtensionManaged
#Produces
#PersistenceUnit
private EntityManagerFactory entityManagerFactory;
}
So I've made a javax.enterprise.inject.spi.Extension, and tryied many ways to do that :
public class TrackableExtension implements Extension {
#Inject #BeforeTrackablePersisted
private Event<Trackable> beforeTrackablePersistedEvent;
#Inject #BeforeTrackableMerged
private Event<Trackable> beforeTrackableMergedEvent;
#SuppressWarnings("unchecked")
public void processEntityManagerTarget(#Observes final ProcessInjectionTarget<EntityManager> event) {
final InjectionTarget<EntityManager> injectionTarget = event.getInjectionTarget();
final InjectionTarget<EntityManager> injectionTargetProxy = (InjectionTarget<EntityManager>) Proxy.newProxyInstance(event.getClass().getClassLoader(), new Class[] {InjectionTarget.class}, new InvocationHandler() {
#Override
public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
if ("produce".equals(method.getName())) {
final CreationalContext<EntityManager> ctx = (CreationalContext<EntityManager>) args[0];
final EntityManager entityManager = decorateEntityManager(injectionTarget, ctx);
return entityManager;
} else {
return method.invoke(injectionTarget, args);
}
}
});
event.setInjectionTarget(injectionTargetProxy);
}
public void processEntityManagerType(#Observes final ProcessAnnotatedType<EntityManager> event) {
final AnnotatedType<EntityManager> type = event.getAnnotatedType();
final AnnotatedTypeBuilder<EntityManager> builder = new AnnotatedTypeBuilder<EntityManager>().readFromType(type);
for (final AnnotatedMethod<? super EntityManager> method : type.getMethods()) {
final String name = method.getJavaMember().getName();
if (StringUtils.equals(name, "persist") || StringUtils.equals(name, "merge")) {
builder.addToMethod(method, TrackableInterceptorBindingLiteral.INSTANCE);
}
}
event.setAnnotatedType(builder.create());
}
public void processEntityManagerBean(#Observes final ProcessBean<EntityManager> event) {
final AnnotatedType<EntityManager> annotatedType = (AnnotatedType<EntityManager>)event.getAnnotated();
// not even called
}
public void processEntityManager(#Observes final ProcessProducer<?, EntityManager> processProducer) {
processProducer.setProducer(decorate(processProducer.getProducer()));
}
private Producer<EntityManager> decorate(final Producer<EntityManager> producer) {
return new Producer<EntityManager>() {
#Override
public EntityManager produce(final CreationalContext<EntityManager> ctx) {
return decorateEntityManager(producer, ctx);
}
#Override
public Set<InjectionPoint> getInjectionPoints() {
return producer.getInjectionPoints();
}
#Override
public void dispose(final EntityManager instance) {
producer.dispose(instance);
}
};
}
private EntityManager decorateEntityManager(final Producer<EntityManager> producer, final CreationalContext<EntityManager> ctx) {
final EntityManager entityManager = producer.produce(ctx);
return (EntityManager) Proxy.newProxyInstance(entityManager.getClass().getClassLoader(), new Class[] {EntityManager.class}, new InvocationHandler() {
#Override
public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
final String methodName = method.getName();
if (StringUtils.equals(methodName, "persist")) {
fireEventIfTrackable(beforeTrackablePersistedEvent, args[0]);
} else if (StringUtils.equals(methodName, "merge")) {
fireEventIfTrackable(beforeTrackableMergedEvent, args[0]);
}
return method.invoke(entityManager, args);
}
private void fireEventIfTrackable(final Event<Trackable> event, final Object entity) {
if (entity instanceof Trackable) {
event.fire(Reflections.<Trackable>cast(entity));
}
}
});
}
}
In all those observer methods, only the second one (processEntityManagerType(#Observes ProcessAnnotatedType<EntityManager>)) is called ! And even with that binding addition to methods persist and merge, my Interceptor is never called (I've of course enabled it with the correct lines in beans.xml, and enabled my extension with the services/javax.enterprise.inject.spi.Extension file).
Something I've thought simple with CDI seems to be actually really hard at last... or perhaps Seam 3 does something which prevent this code from executing correctly...
Does someone know how to handle that ?
I think you're making this a little harder than what it needs to be. Firstly though, JPA and CDI integration isn't very good in Java EE 6, we're very much hoping that changes in Java EE 7 and JPA 2.1.
What you'll want to do is create your own producer for the EntityManager that will delegate to an actual instance of an EntityManager, but also fire your own events when you call the methods you're interested in. Take a look at the Seam Persistence source to see one way this can be done.
As finally my little patch for Seam Persistence was applied in SEAMPERSIST-75, it will be possible in theory to do that by extending org.jboss.seam.persistence.HibernatePersistenceProvider and override the method proxyEntityManager(EntityManager).

Resources