Accessing Java method in xpage repeat control - xpages

I've created a Java Script Library which has a java class that contains some jdbc code.
It has a method to get values from a database (mysql).
Now i need to access it in the repeat control like <xp:repeat value = ?? >
But i don't find a way to access it there.
If it is a javascript library, the method is accessed as <xp:repeat value="#{javascript:getSQLData()}"
How to achieve it? And is it the right approach to use java in script libraries when we also have a separate application named Java inside the Code Section (below script library in the application view).
My java code is:
package com.db;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
public class Db {
Connection con;
Statement st;
ResultSet rs;
public Db(){
this.connect("localhost", "3306", "vijay", "root", "");
try {
this.query("select * from prodet;");
} catch (SQLException e) {
// TODO Auto-generated catch block
System.out.println("///////////////query///////////////////////////////////////////");
e.printStackTrace();
}
}
public void connect(String server, String port, String db, String user, String pwd){
try {
Class.forName("com.mysql.jdbc.Driver");
con=DriverManager
.getConnection("jdbc:mysql://localhost:3306/vijay","root", "");
//con=DriverManager.getConnection("\"jdbc:mysql://"+server+":"+port+"/"+db+"\""+","+"\""+user+"\""+","+"\""+pwd+"\"");
st=con.createStatement();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
List arr = new ArrayList();
#SuppressWarnings("unchecked")
public void query(String q) throws SQLException{
rs=st.executeQuery(q);
while(rs.next()){
arr.add(rs.getString(2));
arr.add(rs.getInt(3));
}
}
public String getData(){
String arra = arr.toString();
return arra;
}
public String doAllDb(){
return this.getData();
}
public static void main(String a[]){
Db d = new Db();
System.out.println(d.getData());
}
}
And the ssjs to access the method is:
importPackage(com.db);
var v = new com.db.Db();
v.doAllDb();
This ssjs is written under Bind data using ssjs.
<xp:repeat id="repeat1" rows="30">
<xp:this.value><![CDATA[#{javascript:importPackage(com.db);
var v = new com.db.Db();
v.doAllDb();}]]>
.
When the xpage is previewed, it is blank. Doesn't show any value. But i tested the java code. It is working fine.

I use managed beans all the time to do exactly that :-)
<xp:repeat rows="10" var="row" value="#{User.rowClubs}">
"User" is my managed bean. It has to be a Java bean that implements the serializable interface, has a constructor without arguments and has public getters/setters for access to properties. In my example the User bean implements the method getRowClubs() that returns a list of objects representing rows of clubs (from the application I worked on this morning).
Please let me know if you need any more help with managed beans? From your question I guessed you needed help on how to reference methods in your bean.
Edit
I just saw the rest of your bean (in the scrollable view). Using your bean you would use something like:
<xp:repeat id="repeat1" rows="30" value="Db.data" var="row">
assuming that you have defined your bean in faces-config.xml, e.g.:
<managed-bean>
<managed-bean-name>Db</managed-bean-name>
<managed-bean-class>com.db.Db</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>
I would also recommend that you remove your "main" method from the bean. If you need to test it from an ordinary Java program then create a test class with a "main" that instantiates and runs your bean.
/John

Related

Pass initialization argument from JSF view to managed bean in background

I am new to JSF and am struggling with passing data to my backing bean. I have a bean that should read and save data to one specific table. This table has two columns "Property" and "Val". The simplified version of this class looks like this:
#ManagedBean
#SessionScoped
public class GlobalProperties implements Serializable {
// private void setKey(param);
// private String getKey();
// some more attributes and getters / setters
private String property; // + getter/setter
private void saveProperty() throws SQLException {
try {
dbHandler = dbConnection.connect();
ps = dbHandler.prepareStatement("UPDATE TABLEXXX SET VAL = '" + getValue() + "' WHERE PROPERTYKEY = '" + getProperty() + "'");
ps.executeQuery();
} catch (SQLException ex) {
Logger.getLogger(GlobalProperties.class.getName()).log(Level.SEVERE, null, ex);
} finally {
if (dbHandler != null) {
dbHandler.close();
}
}
}
private void readProperty() throws SQLException {
try {
dbHandler = dbConnection.connect();
ps = dbHandler.prepareStatement("SELECT VAL FROM TABLEXXX WHERE PROPERTYKEY = '" + getProperty() + "'");
rs = ps.executeQuery();
while (rs.next()) {
setValue("VAL");
}
} catch (SQLException ex) {
Logger.getLogger(GlobalProperties.class.getName()).log(Level.SEVERE, null, ex);
} finally {
if (dbHandler != null) {
dbHandler.close();
}
}
}
}
This class is being used on several pages. On each page it is being used, it needs to be filled with a different value for the property-attribute since I have to read different keys from the table. On JSF-side there is a text input which displays the value and sets it when user clicks a save button.
Whenever an instance of this class is created, I need to pass an information to the class about what key it has to read. But i cannot find an appropriate mechanism on JSF-side to accomplish that, since it is heavily important, that the user can not change that value.
My last try was to use the "preRenderView"-Event like this:
<f:metadata>
<f:event type="preRenderView" listener="#{globalProperties.setKey('SYSTEM_NAME')}" />
</f:metadata>
Is that the right way?
What is the best practice to set a property in a ManagedBean in the background, without the need of any user action and in a safe way, so that it can not be manipulated by the user?
Thank you guys.
<f:event type="preRenderView"> is invoked on every request. That may be OK for request scoped beans, but is unnecessary for view/session scoped beans. See also What can <f:metadata>, <f:viewParam> and <f:viewAction> be used for?
On the other hand, whilst the preRenderView approach will work, the JSF view should be declarative. The model (the backing bean in this specific perspective) should actually be performing the initialization based on the view and not the other way round. So your intuition was right when you got doubts on this.
JSF doesn't have a dedicated approach for this. The closest declarative approach is using <f:attribute> in <f:metadata> ("define a metadata attribute on the view").
<f:metadata>
<f:attribute name="key" value="SYSTEM_NAME" />
</f:metadata>
This is available by UIViewRoot#getAttributes() in a.o #PostConstruct:
String key = (String) context.getViewRoot().getAttributes().get("key");
Or when you happen to use OmniFaces:
String key = Faces.getMetadataAttribute("key");
The OmniFaces showcase application also uses this approach to declare paths to documentation and source code (see for example /push/socket.xhtml source of <o:socket> showcase).
The alternative approach is to have an application wide mapping of keys by view ID and rely on the view ID instead.
Map<String, String> keysByViewId = new HashMap<>();
keysByViewId.put("/foo.xhtml", "SYSTEM_NAME");
String key = keysByViewId.get(context.getViewRoot().getViewId());

How do I use a MethodExpression with parameters for a custom JSF component? [duplicate]

I'm trying to develop a custom component that will need to call a method from the backingbean to get some data from the bb (this will be called in the decode phase after a certain Ajax call) with one parameter (it will come in the ajax call).
The problem I'm having is that I define the attribute as a MethodExpression (in the taglibrary and the component), I get the Ajax post, decode the parameter and when I try to get the Method binding from the component I get the following error:
javax.el.PropertyNotFoundException: /easyFaces.xhtml #19,151
dataSource="#{theBean.loadDataFromSource}": The class
'ar.com.easytech.faces.test.homeBean' does not have the property
'loadDataFromBean'.
Here is the relevant code.. (and please let me know if this is not the correct way to do this..)
taglib:
<attribute>
<display-name>Data Source</display-name>
<name>dataSource</name>
<required>true</required>
<type>javax.el.MethodExpression</type>
<method-signature>java.util.List theDataSource(java.lang.String)</method-signature>
</attribute>
Component definition:
public class Autocomplete extends HtmlInputText implements ClientBehaviorHolder
...
public MethodExpression getDataSource() {
return (MethodExpression) getStateHelper().eval(PropertyKeys.dataSource);
}
public void setDataSource(MethodExpression dataSource) {
getStateHelper().put(PropertyKeys.dataSource, dataSource);
}
and finally the rendered method that generates the error:
private List<Object> getData(FacesContext context, Autocomplete autocomplete, String data) {
Object dataObject = null;
MethodExpression dataSource = autocomplete.getDataSource();
if (dataSource != null) {
try {
dataObject = dataSource.invoke(context.getELContext(), new Object[] {data});
return convertToList(dataObject);
} catch (MethodNotFoundException e) {
logger.log(Level.INFO,"Method not found: {0}", dataSource.getExpressionString() );
}
}
return null;
}
Here is the method from the BB
public List<String> autcompleteFromSource(String param) {
List<String> tmpData = new ArrayList<String>();
tmpData.add("XXA_TABLE_A");
tmpData.add("XXA_TABLE_B");
tmpData.add("XXA_TABLE_C");
return tmpData;
}
And the .xhtml with the component
<et:autocomplete id="autoc" minLength="3" delay="500" value="#{easyfacesBean.selectedValue}" dataSource="#{easyfacesBean.autcompleteFromSource}" />
The thing is if I define a method getAutocompleteFromSource() it recognised the method and the error changes to can't convert list to MethodExpression, so evidently it is simply interpreting the autocompleteFromSource as a simple property and not a method definition, is this even the correct way to call method from BB? (giving that it's not an actual action nor validation )
I found the solution for this, as it turns out you also need to define a "Handler"to define the Method Signature, so I created the handler and added to the taglib and everything started to work fine..just for reference.. here is the handler..
Regards
public class AutocompleteHandler extends ComponentHandler {
public AutocompleteHandler(ComponentConfig config) {
super(config);
}
protected MetaRuleset createMetaRuleset(Class type) {
MetaRuleset metaRuleset = super.createMetaRuleset(type);
metaRuleset.addRule(new MethodRule("dataSource", List.class, new Class[] { String.class }));
return metaRuleset;
}
}

How to use Primefaces Dialog with #ViewScoped setting Parameters?

I'm facing again problem with Dialog.
What I intend to do is a common dialog that will be used for the whole appication, It has its own managed bean that is inherited by other MBs that need to use it, then some parameters are set by example super.setParam(...) to set some data to be displayed.The problem is that when the dialog is being loaded a getter method is called to retrieve the parameters set and it is no longer there, its just null.
I believe that due to the MB being #ViewScoped the container is creating a new instance when the dialog is beying loaded, but the setter method was called before it, so the new instance return to the default values. Using a #SessionScoped would solve the problem but it is not a good choice.
As a work around I tried to set the parameter in the request and then read it in the getter method but it is no longer there either.
Is there some way to get it working?
//here I put the parameter
public void setParam(MyObject myObject) {
FacesContext.getCurrentInstance().getExternalContext().getRequestMap().put("params", myObject);
public MyObject getMyObject() {
// now the parameter is no longer there...
Object objet = FacesContext.getCurrentInstance().getExternalContext().getRequestMap().get("params");
.... after doing some things
return anotherObject;
}
}
EDIT<<<<
All starts with this button on a "client.xhtml"
<p:commandButton value="Call Dialog" ajax="true"
actionListener="#{subDialogMB.startProcess}"
>
</p:commandButton>
#javax.faces.bean.ManagedBean
#javax.faces.bean.ViewScoped
public class SubDialogMB extends SuperDialogMBean() {
public void starProcess() {
try {
MyObject myObject = service.CreateMyObject....
super.setMyObject();
super.shpwDialog();
}
}
#javax.faces.bean.ManagedBean
public class SuperDialogMBean() {
public void setMyObject(MyObject myObject) {
FacesContext.getCurrentInstance().getExternalContext().getRequestMap().put("params", myObject);
}
public MyObject getMyObject(){
public MyObject getMyObject() {
// now the parameter is no longer there...
Object objet = FacesContext.getCurrentInstance().getExternalContext().getRequestMap().get("params");
.... after doing some things
return anotherObject;
}
}
public void showDialog() {
Map<String,Object> options = new HashMap<String, Object>();
options.put("modal", true);
options.put("draggable", false);
options.put("resizable", false);
options.put("contentHeight", 900);
options.put("contentWidth", 1100);
RequestContext.getCurrentInstance().openDialog("myDialog", options, null);
}
}
and finally in the Dialog xhtml
<p:outputText value="#{superDialogMBean.someValue}" />

Cant access property of managed bean from another managed bean

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());
}

Validate rich:dataTable value size's on form submit

I have a "new item" form that requires a list of dates, with the following components:
A <rich:calendar> input;
A <a4j:commandButton> that adds the chosen date to a List<Date> chosenDates in the backing bean;
A <rich:dataTable> with it's value set to the List<Date> chosenDates attribute;
A <a4j:commandButton> per dataTable row that removes it's date from theList<Date> chosenDates;
How to validate (JSF's validation phase) the size of the chosenDates list on form submit (creation process)?
RichFaces 4, JSF 2.1 (Mojarra).
I'd advise a cleaner approach with a JSF PhaseListener. The JSF processing will stop skip ahead the other phases if validation fails. Create a PhaseListener that will inspect the size of your list during the validations phase as against during the model update/invoke action phase. Try something like this
Create a phase listener for the validations phase
public class TestPhaseListener implements PhaseListener {
#Override
public void afterPhase(PhaseEvent event) {
throw new UnsupportedOperationException("Not supported yet.");
}
#Override
public void beforePhase(PhaseEvent event) {
if(event.getPhaseId().equals(PhaseId.PROCESS_VALIDATIONS)){
FacesContext ctx = event.getFacesContext();
YourBeanClass theBeanClass = ctx.getApplication().evaluateExpressionGet(ctx, "#{someBean}", YourNeanClass.class); //obtain a reference to the backing bean containing the list
/*
inspect the size of the list here and based on that throw the exception below
*/
throw new ValidatorException(new FacesMessage("Too many dates","Too Many Dates"));
}
}
#Override
public PhaseId getPhaseId() {
throw new UnsupportedOperationException("Not supported yet.");
}
}
Register your new listener in the faces_config.xml file
<lifecycle>
<phase-listener>your.package.structure.TestPhaseListener</phase-listener>
</lifecycle>
EDIT: Based on your comment, as an alternative, you can hook into the component's lifecycle using the <f:event/> tag and the preValidate or postValidate events (depending on your preference)
A listener tag to your component
<rich:dataTable>
<f:event type="preValidate" listener="#{yourBean.listener}"/>
</rich:dataTable>
Define a listener method in your backing bean to run per your defined event. The method signature must take an argument of type ComponentSystemEvent
public void preCheck(ComponentSystemEvent evt){
//You're in your backing bean so you can do pretty much whatever you want. I'd advise you mark the request as validation failed and queue FacesMessages. Obtain a reference to FacesContext and:
facesContext.validationFailed();
}
Do something like:
#{yourBean.chosenDates.size()}
I suppose you have a getter called getChosenDates which returns the chosenDates list.
Regarding your "validation concerns":
You can create a Validate method in your bean and return list of ValidationMessages. A sample is below, one that i used in my code.
public List<ValidationMessage> validate() {
List<ValidationMessage> validations = new ArrayList<ValidationMessage>();
int curSampleSize = sampleTable.getDataModel().getRowCount();
if(getNumberOfSamples() != null) {
size += getNumberOfSamples();
} else {
validations.add(new ValidationMessage("Please enter the no of samples to continue."));
return validations;
}
return validations;
}
Then, on submit you can check if you have any ValidationMessages as follows:
List<ValidationMessage> errs = validate();
if(errs.size()>0) {
FacesValidationUtil.addFacesMessages(errs);
return null;
}
Hope this helps!

Resources