I need help for the cooperation between #EJB and #CDI
Hi all,
I would like to have the following scenario:
1) In my app a Notification is created (in database)
2) Afterwards a Push Notification should be send to the specific client
3) In Client it will update a specific #form from my page...
Here is my code:
#Stateless
public class NotificationCreationSendServiceBean implements NotificationCreationSendService {
#Inject
private BeanManager beanManager;
public void createNotification {
// createNotificationInDatabase();
.....
PushEvent event = new PushEvent("Test");
beanManager.fireEvent(event);
}
}
My JSF Bean:
import static org.omnifaces.util.Messages.addGlobalError;
import static org.omnifaces.util.Messages.addGlobalInfo;
#Named
#ViewScoped
public class NotificationSocket implements Serializable {
#Inject
private LoginBean loginBean;
#Inject
#Push(channel = "notificationChannel")
private PushContext push;
/**
* Push Notification
*
* #param recipientUser
*/
public void pushUser(#Observes PushEvent event) {
Set<Future<Void>> sent = push.send(event.getMessage(), loginBean.getCurrentEmployee().getId());
if (sent.isEmpty()) {
addGlobalError("This user does not exist!");
} else {
addGlobalInfo("Sent to {0} sockets", sent.size());
}
}
}
In here JSF page:
<o:socket channel="notificationChannel"
user="#{loginBean.currentEmployee.id}" scope="view">
<f:ajax event="someEvent" listener="#{bean.pushed}" render=":notificationLink" />
</o:socket>
My question is now:
How is my #EJB container recognized with Socket is the right one? Where do I define the channel name in #EJB?
Can anybody help me, please.
How to send push via o:socket from EJB to Client?
This title is strange as your question already shows the code which does exactly that right.
How is my #EJB container recognized with Socket is the right one? Where do I define the channel name in #EJB?
This specific question is really strange in the current context. I can only assume that you actually have multiple #Observes PushEvent methods and that you actually wanted to target only a specific method which is associated with a specific #Push channel. Only in that context this question would make somewhat sense.
Well, in order to achieve that, there are several ways.
Pass it as an argument/property of the PushEvent class:
beanManager.fireEvent(new PushEvent("notificationChannel", "Test"));
And then just check for that in your observer method:
if ("notificationChannel".equals(event.getChannelName())) {
// ...
}
Feel free to use enums instead.
Or, create a specific class for every specific event:
beanManager.fireEvent(new NotificationEvent("Test"));
And then just make sure you observe it in only one method:
public void pushUser(#Observes NotificationEvent event) {
// ...
}
Or, create a #Qualifier for the PushEvent:
#Qualifier
#Retention(RUNTIME)
#Target({ FIELD, PARAMETER })
public #interface Notification {}
Which you #Inject via Event<T>:
#Inject #Notification
private Event<PushEvent> pushEvent;
public void createNotification {
pushEvent.fire(new PushEvent("Test"));
}
And then just make sure you observe it in only one method:
public void pushUser(#Observes #Notification PushEvent event) {
// ...
}
Related
I am working whith JSF (Primeface) and j2ee on weblogic.
So, i have two different flows in my application:
Flow configuration:
public class RequestFlow implements Serializable {
#Produces
#FlowDefinition
public Flow defineFlow(#FlowBuilderParameter FlowBuilder flowBuilder) {
String flowId = "requestFlow";
flowBuilder.id("", flowId);
flowBuilder.viewNode(flowId, "/inside/customer/request/flow/requestFlow.xhtml").markAsStartNode();
flowBuilder.viewNode("requestFlowCart", "/inside/customer/request/flow/requestFlowCart.xhtml");
flowBuilder.viewNode("requestFlowCheckout", "/inside/customer/request/flow/requestFlowCheckout.xhtml");
flowBuilder.returnNode("finishRequest").fromOutcome("/inside/customer/request/requests.xhtml");
return flowBuilder.getFlow();
}
}
CDI's flow bean:
#Named
#FlowScoped("requestFlow")
public class RequestFlowBean implements Serializable {
//some logic
}
Second configuration:
public class OrderFlow implements Serializable {
#Produces
#FlowDefinition
public Flow defineFlow(#FlowBuilderParameter FlowBuilder flowBuilder) {
String flowId = "orderFlow";
flowBuilder.id("", flowId);
flowBuilder.viewNode(flowId, "/inside/customer/order/flow/orderFlow.xhtml").markAsStartNode();
flowBuilder.viewNode("orderFlowSelectRequests", "/inside/customer/order/flow/orderFlowSelectRequests.xhtml");
flowBuilder.viewNode("orderFlowReviewRequests", "/inside/customer/order/flow/orderFlowReviewRequests.xhtml");
flowBuilder.viewNode("orderFlowCheckoutOrder", "/inside/customer/order/flow/orderFlowCheckoutOrder.xhtml");
flowBuilder.returnNode("finishOrder").fromOutcome("/inside/customer/order/orders.xhtml");
return flowBuilder.getFlow();
}
}
CDI's flow bean:
#Named
#FlowScoped("orderFlow")
public class OrderFlowBean implements Serializable {
//some logic
}
My Case:
User opens page where by clicking h:button starts the "requestFlow" (doesn't finish it!)
Using menu navigates to another page, by clicking h:button tries to start the "orderFlow".
Problem:
"OrderFlow" wasn't start without any error in console! And the first flow still in memory, but according documentation it have to be destroyed.
So, I want to be able create a new FlowScoped bean when other one was not finished.
Any suggestions?
So, exactly in 2 month i found the answer.
The trick is how you start your flow. If you want to run new JSF flow, while didn't finish other one, you have to remove from JSF context previous instances of any flow. In order to do it, you have add method in controller:
public String initFlow() {
FacesContext context = FacesContext.getCurrentInstance();
FlowHandler handler = context.getApplication().getFlowHandler();
ExternalContext extContext = context.getExternalContext();
String sessionKey = extContext.getClientWindow().getId() + "_flowStack";
Map<String, Object> sessionMap = extContext.getSessionMap();
if (sessionMap.containsKey(sessionKey)) {
sessionMap.remove(sessionKey);
}
handler.transition(context, null, handler.getFlow(context, "", getFlowName()), null, "");
return getFlowName();
}
And start flow page in the next way:
<p:commandButton value="Start Flow"
action="#{controller.initFlow}"/>
</p:panelGrid>
I am trying to achieve the following :
EJB3 Singleton
#Singleton
#Startup
public class SomeSingleton implements SomeSingletonLocal {
// Entity Manager injection
private EntityManager _entity_manager;
#Override
#Asynchronous
public void createScenario(){
method1();
method2();
// ...
}
public void method1(){
// Persist an Event in a Database.
}
public void method2(){
// Persist an Event in a Database.
}
}
Managed Bean
#ManagedBean
#RequestScoped
public class SomeManagedBean{
// Entity Manager injection
private EntityManager _entity_manager;
#EJB
private SomeSingletonRemote _singleton;
public void createScenario(){
_singleton.createScenario();
}
public List<Event> getEventList(){
// Retrieve events from database
}
}
JSF view
<h:form>
<p:commandButton value="Start Long Stuff"
actionListener="#{SomeManagedBean.createScenario}" />
<h:outputText id="count" value="#{SomeManagedBean.getEventList.size()}" />
<p:poll interval="1" update="count" />
</h:form>
log
->SomeManagedBean.getEventList()
<-SomeManagedBean.getEventList() // Size = 0
// Buton clicked
->SomeManagedBean.createScenario()
->SomeSingleton.createScenario()
<-SomeManagedBean.createScenario()
->SomeManagedBean.getEventList() // will end at the end of SomeSingleton.createScenario
->SomeSingleton.method1()
<-SomeSingleton.method1() // persist
...
->SomeSingleton.methodN()
<-SomeSingleton.methodN() // persist
<-SomeSingleton.createScenario()
<-SomeManagedBean.getEventList() // size = N
I expected at least one call to getEventList between two methodI() call (ie. each second). When it enters in SomeSingleton.createScenario(), I dont know why getEventList is paused.
It looks like there is a lock with the entity manager or the transaction inside createScenario. Is that a reentrance problem ?
A #Singleton is indeed by default read/write locked. This is not strictly related to transactions, but to concurrency. See also a.o. Java EE 7 tutorial on the subject.
One way to solve it is to set the #ConcurrencyManagement to BEAN. This way you basically tell the container to not worry about concurrency at all and that you take all responsibility on your own.
#Singleton
#ConcurrencyManagement(ConcurrencyManagementType.BEAN)
public class SomeSingleton {}
Another way is to explicitly set #Lock to READ on the class or the read-only methods so that they can concurrently be invoked. Only when a method with an explicit #Lock(LockType.WRITE) is invoked on the same instance, then the lock will occur.
#Singleton
#Lock(LockType.READ)
public class SomeSingleton {}
I want to access the property of a #SessionScoped bean in another bean using #ManagedProperty. In short, I want to access the name property of firstBean in secondBean.
#ManagedBean
#SessionScoped
public class FirstBean implements Serializable{
private String name;
//...other attributes
//...constructor
public String getSelectedModel() {
return selectedModel;
}
public void setSelectedModel(String selectedModel) {
this.selectedModel = selectedModel;
}
//other getters&setters
}
And second bean:
#ManagedBean
#SessionScoped
public class SecondBean implements Serializable{
#ManagedProperty(value="#{firstBean}")
private FirstBean firstBean
public SecondBean() {
System.out.println(firstBean.getName());
}
public IndexBean getFirstBean() {
return firstBean;
}
public void setFirstBean(FirstBean firstBean) {
this.firstBean = firstBean;
}
When I run this, I always get NullPointerException on System.out.println(firstBean.getName()); in the constructor of second bean, which seems to mean that I need to create a new instance of firstBean.
But strangely, when I commented out this line, I can do something like this with no errors, which means that firstBean is actually a property of secondBean.
<h:outputText value="#{secondBean.firstBean.name}" />
What's the problem here?
It's not possible to access an injected dependency in the constructor. You're basically expecting that Java is able to do something like this:
SecondBean secondBean; // Declare.
secondBean.firstBean = new FirstBean(); // Inject.
secondBean = new SecondBean(); // Construct.
It's absolutely not possible to set an instance variable if the instance is not constructed yet. Instead, it works as follows:
SecondBean secondBean; // Declare.
secondBean = new SecondBean(); // Construct.
secondBean.firstBean = new FirstBean(); // Inject.
Then, in order to perform business actions based on injected dependencies, use a method annotated with #PostConstruct. It will be invoked by the dependency injection manager directly after construction and dependency injection.
So, just replace
public SecondBean() {
System.out.println(firstBean.getName());
}
by
#PostConstruct
public void init() { // Note: method name is fully to your choice.
System.out.println(firstBean.getName());
}
I have a EJB bean that exposed to two interface like below: Local interface is for my web app, and Remote interface is for my App Client
#Stateless
public class CoreMainEJB implements CoreMainEJBRemote, CoreMainEJBLocal {
//...
}
so my App Client looks as below. Remote method invocation is happened in this case
public class Main {
#EJB
private static CoreMainEJBRemote coreEJBRemote;
public static void main(String[] args) {
coreEJBRemote.process(args[0]);
}
}
From my web app I invoke as below. Local method invocation is happened in this case
#ManagedBean
#RequestScoped
public class DisplayInbound {
#EJB
private CoreMainEJBLocal coreMainEJBLocal;
public void processPackages() {
coreMainEJBLocal.process(...);
}
}
So here is my question, If the EJB only exposed #Remote interface, but in your web app, you inject the EJB bean directly instead of its Remote interface, will this trigger a remote invocation or local invocation? For example:
#Stateless
public class CoreMainEJB implements CoreMainEJBRemote{
//...
}
and in the web app, I do this
#EJB
private CoreMainEJB coreMainEJB;
public void processPackages() {
coreMainEJB.process(...); //Is this local or remote invocation here?
}
The last example as given will simply not work. Since CoreMainEJB already implements a remote interface, the container will not create a No-Interface view. This is exactly the case for which #LocalBean is intended.
So to answer the question 'Is this local or remote invocation here?' directly: it's neither. The container will not be able to inject anything and probably barf out at the deployment stage.
If you define your bean as:
#Stateless
#LocalBean
public class CoreMainEJB implements CoreMainEJBRemote{
//...
}
Then local semantics will apply here:
#EJB
private CoreMainEJB coreMainEJB;
public void processPackages() {
coreMainEJB.process(...); // Local semantics
}
(assuming the above code fragment is in the same application as where CoreMainEJB is defined of course)
A no-interface invocation is a local invocation.
A simple question on the title.
My case is that I want to listen to "before RENDER_RESPONSE" phase, and alter some components internal state.
Is PhaseListener the "right way" to do this in SEAM applications?
If you want alter JSF component internal state, rely on JSF phase listener. Seam way of declaring JSF phase listener is shown bellow
#Name("applicationPhaseListener")
#Scope(ScopeType.APPLICATION)
public class ApplicationPhaseListener {
/**
* Called TRANSPARENTLY by Seam
*/
#Observer("org.jboss.seam.beforePhase")
public void beforePhase(PhaseEvent event) {
}
/**
* Called TRANSPARENTLY by Seam
*/
#Observer("org.jboss.seam.afterPhase")
public void afterPhase(PhaseEvent event) {
}
}
But if you want to alter Seam contextual component state, use
#Name("applicationPhaseListener")
public class ApplicationPhaseListener {
#Observer("applicationListener")
public void applicationListener() {
}
}
You can
Call your event programatically
Events.instance().raiseEvent("applicationListener");
By using #RaiseEvent annotation which is placed aboved some action method
#RaiseEvent("applicationListener")
public void doSomething() {
}
pages.xml
<page id="<PAGE_ID_GOES_HERE>">
<raise-event type="applicationListener"/>
</page>