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).
Related
I am trying to get IEventBroker injected into my code to send out notifications.
Everything else works but eventBroker never gets injected. I do not get any compile time errors.
It just comes up null when the code is executed.
I've trimmed the code because it wouldn't let me submit it.
Thanks for any help in advance!
package com.test.services.internal;
imports ...
#Component
public class EnvironmentServiceImpl implements IEnvironmentService {
#Inject
private IEventBroker eventBroker;
private EntityManagerFactory entityManagerFactory;
private EntityManager entityManager;
#Activate
#SuppressWarnings("unchecked")
protected void activateComponent() {
getAll(environments -> {
if (environments.isEmpty()) {
List<Environment> initialModel = getMockEnvironments();
initialModel.forEach(this::save);
}
});
}
#Deactivate
protected void deactivateComponent() {
}
#Override
public void getAll(Consumer<List<Environment>> taskConsumer) {
eventBroker.post(EnvironmentEventConstants.TOPIC_ENVIRONMENT_LOADED,
createEventData(EnvironmentEventConstants.TOPIC_ENVIRONMENT_LOADED, updateEnvironment.getId()));
}
private Map<String, String> createEventData(String topic, String environmentId) {
}
}
So, I have this page:
#Named("ManagementPage")
#ViewScoped
#Getter
#Setter
#Join(path = "/{appScope}/admin/management",
to = "/pages/scoped/managementOverview.xhtml")
#Page(
group = "kitchen",
icon = "mdi mdi-comment-text",
key = "management",
navigation = Page.Navigation.ADMIN_SCOPED,
outcome = "/pages/scoped/managementOverview.xhtml",
auth = #PageAuth(value = "MANAGER_ACCESS", scoped = true))
public class ManagementPage implements Serializable {
private static final long serialVersionUID = 1L;
#Inject
private ManagementModel model;
#PostConstruct
public void init() {
this.model.init();
}
}
It's ViewScoped. And the model for it is:
#Log4j
#Dependent
#Getter
#Setter
public class ManagementModel implements Serializable {
...
}
I want, whenever I receive an event, to refresh some UI on the frontend (I'm using JSF). For that, I've created this dispatcher:
#ApplicationScoped
public class OrderEventDispatcher {
private static final List<ManagementModel> subscriptions = new ArrayList<>();
public static void addSubscriber(ManagementModel subscriber) {
subscriptions.add(subscriber);
}
public static void removeSubscriber(ManagementModel subscriber) {
subscriptions.remove(subscriber);
}
public void observerOrderCreated(#Observes FrontendEvent frontendEvent) {
if(frontendEvent instanceof ContentItemCreatedEvent){
if(!"order".equals(((ContentItemCreatedEvent) frontendEvent).getTypeKey())){
return;
}
}
if(frontendEvent instanceof ContentItemChangedEvent){
if(!"order".equals(((ContentItemChangedEvent) frontendEvent).getTypeKey())){
return;
}
}
subscriptions.forEach(ManagementModel::orderInit);
}
}
(I have implemented a proper equals for this in my model)
For my dispatcher to work, I'm subcribing with my model to it (the methods are inside the model)
#PostConstruct
public void init() {
id = totalIds++;
OrderEventDispatcher.addSubscriber(this);
...
And then i unsubscribe before I destroy the model:
#PreDestroy
public void preDestroy() {
OrderEventDispatcher.removeSubscriber(this);
}
And finally, the methods I call from my dispatcher:
public void orderInit() {
loadMergedOrders();
initializeDonut();
PrimeFaces.current().executeScript("orderInit()");
}
I'm doing all this in order to refresh my page (even when multiple instance of the same page are open) in reaction to an event (some item is created/deleted/modified, of that the FrontendEvent takes care). Now the issue is that my PrimeFaces.current() is always returning null, I've added a breakpoint in the init() method and I tried using PrimeFaces.current() and it worked then, but then when I went through the Dispatcher and into the orderInit() with the debugger I've seen that PrimeFaces.current() now returns null. Does anyone have any idea what I'm doing wrong? If not how to fix this then maybe a different approach to solving this. Thanks for your time!
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.
I need to share an attribute between the beforePhase() and the afterPhase() methods of my PhaseListener, for a same JSF request.
Is the following snippet thread-safe?
public class MyPhaseListener implements PhaseListener {
private MyObject o = null;
#Override
public void beforePhase(PhaseEvent event) {
if (condition) {
o = new MyObject();
}
}
#Override
public void afterPhase(PhaseEvent event) {
if (o != null) {
o.process();
o = null;
}
}
#Override
public PhaseId getPhaseId() {
return PhaseId.RESTORE_VIEW;
}
}
If not, what are other solutions?
This is definitely not threadsafe. There's only one phase listener instance applicationwide which is shared across multiple requests. Basically, a phase listener is like an #ApplicationScoped managed bean.
Just set it as a context attribute.
public class MyPhaseListener implements PhaseListener {
#Override
public void beforePhase(PhaseEvent event) {
if (condition) {
event.getFacesContext().setAttribute("o", new MyObject());
}
}
#Override
public void afterPhase(PhaseEvent event) {
MyObject o = (MyObject) event.getFacesContext().getAttribute("o");
if (o != null) {
o.process();
}
}
#Override
public PhaseId getPhaseId() {
return PhaseId.RESTORE_VIEW;
}
}
You could use ThreadLocal for this, but it tends to have issues in environments having different classloaders, to name it: memory leak. Be sure to check for that in the given environment...
Also, you should make it sure that if the processing can be interrupted (e.g. exception...) between the beforePhase() and afterPhase() methods, the ThreadLocal should be handled appropriately...
This is what it would look like:
public class MyPhaseListener implements PhaseListener {
//if null is a valid value, no initial setting is needed
private ThreadLocal<Object> myStateObject = new ThreadLocal<Object> ();
#Override
public void beforePhase(PhaseEvent event) {
//might be needed, to guarrantee no residue from an aborted processing is in there
myState.set(null);
if (condition) {
myState.set(<Object representing the state>);
}
}
#Override
public void afterPhase(PhaseEvent event) {
try {
Object stateObject = myState.get();
if (stateObejct!=null) {
//do what you have to
}
} finally {
//to be sure
myState.remove();
}
}
}
In this article the author uses ThreadLocal too...
Also, this article is also a great eye-opener, explaining why not to share mutable instance-level information:
One thing to remember though, is that PhaseListener instances are application-wide Singletons that are referenced by the JSF Lifecycle, which itself is an application-wide Singleton.
EDIT just saw Boolean got updated to Object, adjusted example
I am trying to build an JSF library control for XPages based on the examples by Keith Strickland.
http://xprentice.gbs.com/A55BAC/keithstric.nsf/default.xsp?documentId=82770C11FA7B9B21852579C100581766
I'm having a little bit trouble in building a FileDownloadControl
Here is the code I've built:
public class Libcontrol extends UIComponentBase implements FacesComponent {
private static final String RENDERER_TYPE = "de.chris.Libcontrol ";
private static final String COMPONENT_FAMILY = "de.chris";
public Libcontrol() {
setRendererType(RENDERER_TYPE);
}
#Override
public String getFamily() {
return COMPONENT_FAMILY;
}
#SuppressWarnings("unchecked")
public void initBeforeContents(FacesContext arg0) throws FacesException {
FacesContext context;
ExpressionEvaluatorImpl evaluator;
context = FacesContext.getCurrentInstance();
evaluator = new ExpressionEvaluatorImpl(context);
XspFileDownload result = new XspFileDownload();
String sourceId = "fileDownload1/#value";
String valueExpr = "#{document1.FileField}";
ValueBinding value = evaluator.createValueBinding(result, valueExpr, sourceId,Object.class);
result.setValueBinding("value", value);
result.setDisplayLastModified(true);
result.setAllowDelete(true);
result.setTitle("filedown");
result.setRows(30);
result.setId("fileDownload1");
this.getChildren().add(result);
}
public void buildContents(FacesContext arg0, FacesComponentBuilder arg1) throws FacesException {
// Do Nothing
}
public void initAfterContents(FacesContext arg0) throws FacesException {
// Do nothing
}
}
Why is the control not completely rendered? When I look to the HTML Code I see a starttag from the control but no Files to download
and yes I've uploaded files to the corresponding NotesDocument.
Here is the renderer I have implmented respectively copied:
public class MainLibcontrolRenderer extends Renderer {
#Override
public void encodeBegin(FacesContext context, UIComponent component) {
try {
super.encodeBegin(context, component);
context = FacesContext.getCurrentInstance();
UIViewRootEx rootEx = (UIViewRootEx) context.getViewRoot();
/*rootEx.setDojoParseOnLoad(true);
rootEx.setDojoTheme(true);*/
ResponseWriter writer = context.getResponseWriter();
writer.startElement("fieldset", component);
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void encodeChildren(FacesContext context, UIComponent component) {
try {
super.encodeChildren(context, component);
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void encodeEnd(FacesContext context, UIComponent component) {
try {
super.encodeEnd(context, component);
ResponseWriter writer = context.getResponseWriter();
writer.endElement("fieldset");
} catch (Exception e) {
e.printStackTrace();
}
}
}
Stephan is right: the reason the contents do not render is that you're not building them. When implementing FacesComponent, the buildContents method typically should instruct the FacesComponentBuilder to initiate the build process; e.g.:
arg1.buildAll(arg0, this, true);
NOTE: I'm using the argument names from your example; ideally, you should use meaningful argument names like "context" and "builder".
The buildAll method referred to above causes the component tree to properly reflect any changes made to the structure during the init methods. If you skip this step, the subsequent JSF phases (including RENDER_RESPONSE) are unaware of any components you injected.
By the way, Keith also makes a valid point: hardcoding the value binding and other properties - at least, in the example you provided - tends to defeat the purpose of defining a reusable control. I'd echo Keith's advice to take a closer look at what you're trying to accomplish to determine whether a custom component is really the appropriate implementation. And one final caution: use extreme care when programmatically setting the id property on injected components... you could end up with a name collision that cannot be detected during compilation. In other words, Designer can't warn you... it will just break at runtime and the reason for failure will probably not be obvious.