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>
Related
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) {
// ...
}
JSF2, Primefaces 3.3.FINAL, Spring
I have ApplicationScoped Bean for loading all dropdown data during start up of server. I referenced the same bean via ManagedProperty annotation. But when switching views the bean gets destroyed and i need to recreate the bean and data again.
Sample Code:
#ManagedBean(name = "refDataBean", eager = true)
#ApplicationScoped
public class RefDataBean extends AbsBackingBean implements Serializable{
....
#PostConstruct
public void init(){
//load multiple drop down data - populateData-db call
}
}
#ManagedBean(name = "searchViewBean")
#ViewScoped
public class SearchViewBean{
#ManagedProperty(value = "#{refDataBean}")
private RefDataBean refDataBean;
#PostConstruct
public void init() { //getUser object }
public User retrieveUser(User user) {
List<User> userList = refDataBean.getUserList();
}
public PICTSRefDataBean getPictsRefDataBean() {
return pictsRefDataBean;
}
public void setPictsRefDataBean(final PICTSRefDataBean pictsRefDataBean)
{ this.pictsRefDataBean = pictsRefDataBean; }
}
In one page/view, user.xhtml
<h:selectOneMenu value="#{searchViewBean.selectedUser}" >
<f:selectItems value="#{refDataBean.userList}" var="taskUser"
itemLabel="#{taskUser.fullNameAndId}"
itemValue="#{taskUser.networkLogin}"></f:selectItems>
</h:selectOneMenu>
When switching to another page, team.xhtml, the refDataBean is null and it goes to PostConstruct method of RefDataBean constructing the whole drop down list again. Since its a static data, i expect load it once and should be able to access in any page. I know that Objects in View scope are destroyed when you switch to a different view. How to restrict that? Or what am i missing? Please help
I'm beginning with JSF (Mojarra 2.2 and Glassfish 4) and currently practicing with a web application which job is to store Clients and their Orders in DB.
When creating a new Order, one feature is to allow choosing an existing client from a JSF <h:selectOneMenu>. An Order entity stores a Client entity among other attributes...
I've followed BalusC's great answer about prepopulating a <h:selectOneMenu> from a DB (here), and have successfully populated mine from data stored in an eager ApplicationScoped ManagedBean, but I can't manage to retrieve the selected item in the backing bean as complex object. It is always null.
This is driving me mad and your help will be truly appreciated!
Here are the relevant code snippets:
#ManagedBean(eager = true)
#ApplicationScoped
public class Data implements Serializable {
private static final long serialVersionUID = 1L;
#EJB
private ClientDao clientDao;
private List<Client> clients;
#PostConstruct
private void init() {
clients = clientDao.lister();
}
public List<Client> getClients() {
return clients;
}
}
Order creation bean (note: 'commande' means order ;)
#ManagedBean
#RequestScoped
public class CreerCommandeBean implements Serializable {
private static final long serialVersionUID = 1L;
private Commande commande;
private String choixNouveauClient = "nouveauClient";
#EJB
private CommandeDao commandeDao;
public CreerCommandeBean() {
commande = new Commande();
}
public void inscrire() {
System.out.println("client : " + commande.getClient()); // prints **NULL**
// ... orderService to store in DB
}
... getters and setters
Client converter:
#FacesConverter(value = "clientConverter", forClass = Client.class)
public class ClientConverter implements Converter {
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
if (value == null) {
return null;
}
Data data = context.getApplication().evaluateExpressionGet(context, "#{data}", Data.class);
for (Client c : data.getClients()) {
if (c.getId().toString().equals(value)) {
return c;
}
}
throw new ConverterException(new FacesMessage(String.format("Cannot convert %s to Client", value)));
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
return (value instanceof Client) ? String.valueOf(((Client) value).getId()) : null;
}
}
Facelet excerpt:
<p:outputPanel id="gridContainerAncienClient">
<p:selectOneMenu value="#{creerCommandeBean.commande.client}"
rendered="#{creerCommandeBean.choixNouveauClient == 'ancienClient'}">
<f:converter converterId="clientConverter" />
<f:selectItems value="#{data.clients}" var="cli"
itemValue="#{cli}" itemLabel="#{cli.prenom} #{cli.nom}" />
</p:selectOneMenu>
</p:outputPanel>
CreerCommandeBean is #RequestScoped. That means it will live only for one request.
When you select a client to be assigned to #{creerCommandeBean.commande.client} you do this by a request. #{creerCommandeBean.commande.client} is now the selected client. Then the request is over, the bean gets destroyed and your "changes" are lost.
When you try to retrieve that data, you do that by a request again: A new instance of CreerCommandeBean is created and the constructor assigns the property commande with a new instance of Commande whose property client again is probably null.
Solution:
Use a broader scope. e.g. #ViewScoped which makes the bean "live" as long as you stay in the same view - no matter how many requests you make.
Tip:
Read BalusC's Post on Communication is JSF 2.0. Parts might be slightly different in JSF 2.2 but it's still a good and comprehensive introduction.
I got stuck with similar problem, only to realize that I forgot to implement equals() and hashCode() method in my Object. Client Class in this case.
I should blame myself for skipping the instructions in BalusC's blog.
"...Please note the Object#equals() implementation. This is very important for JSF. After conversion, it will compare the selected item against the items in the list. As the Object#equals() also require Object#hashCode(), this is implemented as well...."
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 problem I don't understand: Behind any View I have a controller ManagedBean that is RequestScoped and a data ManagedBean, that holds the data for the view and is SessionScoped.
So there are two views, which are login with loginData and loginController and overview with overviewData and overviewController.
The functionality should be like that:
The User logs into the application (loginController method)
If Authentication is successfull, there is a redirect to overview.xhtml (again in loginController method)
Then the overviewData gets its data by the overviewController, which retrieves them from business logic layer
The overview.xhtml shows the retireved data
So, the point is that I want to fill overviewData out of loginController, right after login! (???or if possible right befor overview view is constructed, if possible???).
I tried it with managedProperties, but the one I initiate in loginController is a different object than the managedProperty in overviewController, although they have the same name! How is that possible.
Oh boy, I doubt you guys understand what I mean, so I need to post some code:
LoginController.java
...
#ManagedBean
#RequestScoped
public class LoginController {
#ManagedProperty(value = "#{overviewData}")
private OverviewData overviewData;
OverviewController overviewController;
public LoginController(){
overviewController = new OverviewController ();
}
String login() throws Exception {
UsernamePasswordToken token = new UsernamePasswordToken(loginData.getName(), loginData.getPw().trim());
try {
currentUser.login(token);
overviewController.fillProjects();
...
OverviewController.java
...
#ManagedBean
#RequestScoped
public class OverviewController {
#ManagedProperty(value = "#{overviewData}")
private OverviewData overviewData;
public void fillProjects(){
if(overviewData == null){
overviewData = new OverviewData();
}
overviewData.setProjects(projectService.getProjects()); //retrieves data from business logic
}
...
OverviewData.java
...
#ManagedBean(name = "overviewData")
#SessionScoped
public class OverviewData {
private List<ProjectDTO> projects; //that's what the view needs to display the overview
public void setProjects(List<ProjectDTO> projects) {
this.projects = projects;
}
...
I hope that helps to show my problem, if you don't understand it, pls ask in a comment..
Would be nice if you can help me :-)
Cheers...
You're creating beans yourself using new instead of letting JSF do the job.
overviewController = new OverviewController ();
and
overviewData = new OverviewData();
This is wrong. JSF won't utilize any beans which you've created yourself this way. Remove those lines and add another #ManagedProperty on overviewController inside LoginController (and make the property private).
#ManagedProperty(value="#{overviewController}")
private OverviewController overviewController;
JSF will create the bean itself and set it as managed property directly after parent bean's construction. You just have to access it the usual Java way (without the need for nullchecks).