Dynamically creating a JSF form with java reflection - java

I'm trying to create a simple crud form to insert data into a database with hibernate, without knowing what the object type is. The ultimate goal is to only have one insert form for every table in the database. So far i get the methods that the current object has, check to see if it has any set methods and create a text input for every field that has a set.
UIViewRoot viewRoot = FacesContext.getCurrentInstance().getViewRoot();
HtmlPanelGrid hpg = (HtmlPanelGrid) viewRoot.findComponent("panel");
for (Method method : declaredFields) {
String name = method.getName();
if (name.contains("set")) {
HtmlOutputText hot = new HtmlOutputText();
HtmlInputText hit = new HtmlInputText();
hot.setValue(name.substring(3));
try {
hit.setValue(newObject.getClass().getMethod(name, String.class));
} catch (Exception ex) {
Logger.getLogger(ReflectController.class.getName()).log(Level.SEVERE, null, ex);
}
hpg.getChildren().add(hot);
hpg.getChildren().add(hit);
}
}
Here newObject is the object that is going to be inserted into the database later with hibernate. My problem is this:
How do assign a certain field from that object to the text input that is being created at the moment. So far if I put the method in the value like I'm doing above, it will just print out the method in the value attribute for that input. what i want is that when this form is submited, for to assign the value in that text box to the property with that name.

I can give you a partial answer - You need to create a ValueExpression dynamically
Application app = FacesContext.getCurrentInstance().getApplication();
hit.setValueExpression("value", app.getExpressionFactory().createValueExpression(FacesContext.getCurrentInstance().getELContext(), "#{bean.item}", Item.class));
The hard part will be creating the valueExpression that will actually map to a field within your object's value. That requires a great deal more thought but you will for sure need the dynamic valueExpression. As written, this will result in the execution of your bean's setItem();method with a parameter of type Item. You will require something a little more complex.

In JSF, binding input components to properties is accomplished with EL-expressions. You can create one programmatically as Steve shows, but that syntax is really ugly. On a related note, programmatic manipulation of the component tree is a rather unorthodox way of using JSF. The orthodox way to tackle your requirement would be something like:
<ui:repeat var="prop" value="#{genericEditorBean.propertyNames}">
<h:outputLabel value="#{prop}" for="input"/>
<h:inputText id="input" value="#{genericEditorBean.object[prop]}"/>
</ui:repeat>
where
public List<String> getPropertyNames() {
List<String> propertyNames = new ArrayList<>();
BeanInfo beanInfo = Introspector.getBeanInfo(object.getClass());
for (PropertyDescriptor pd : beanInfo.getPropertyDescriptors()) {
propertyNames.add(pd.getName());
}
return propertyNames;
}
(There really is no reason to reimplement scanning for Java Bean properties when the Java API offers a class for that very purpose. Unlike your home-grown version, this will also handle properties inherited from a super class ...)

I once used an open-source library named MetaWidget to do this.
It was a few years ago, but it worked well and was easy to set up.
It looks like the project is still active:
http://metawidget.sourceforge.net/index.php

Related

Creating JsonLd + Hydra based Generic Client API in java. Is there any projects exist for reference?

I am creating Client API in Java using :+ Apache Jena FrameWork+ Hydra(for Hypermedia driven) + my private vocab similar to Markus Lanther Event-API Vocab instead of schema.org(for Ontology/Vocabulary part)
Section 1 :
After looking this Markus Lanther EventDemo repo and hydra-java.I found that they are creating classes for each hydra:Class that can break client in future .For example :
A Person class (Person.java)
public class Person
{
String name;
};
But in future requirement name is also a class eg:
public class Name
{
String firstName;
String LastName;
};
So to fulfill this requirement I have to update Person class like this:
public class Person
{
Name name;
};
Question 1:
Is my understanding correct or not of this Section? If yes then what is the way to deal with this part ?
Section 2:
To avoid above problem I created a GenericResource class(GenericResource.java)
public class GenericResource
{
private Model model;
public void addProperty(String propertyName,Object propertyValue)
{
propertyName = "myvocab:"+propertyName;
//Because he will pass propertyName only eg: "name" and I will map it to "myvocab:name"
//Some logic to add propertyName and propertyValue to model
}
public GenericResource retriveProperty(String propertyName)
{
propertyName = "myvocab:"+propertyName;
//Some logic to query and retrieve propertyName data from this Object add it to new GenericResource Object and return
}
public GenericResouce performAction(String actionName,String postData)
{
//Some logic to make http call give response in return
}
}
But again I stuck in lots of problem :
Problem 1: It is not necessary that every propertyName is mapped to myvocab:propertyName. Some may be mapped to some other vocab eg: hydra:propertyName, schema:propertyName, rdfs:propertyName, newVocab:propertyName, etc.
Problem 2: How to validate whether this propertyName belongs to this class ?
Suggestion: Put type field/variable in GenericResource class.And then check supportedProperty in vocab corresponding to that class.To more clarity assume above Person class which is also defined in vocab and having supportedProperty : [name,age,etc] .So my GenericResource have type "Person" and at time of addProperty or some other operation , I will query through vocab for that property is in supportedProperty list or in supportedOperation list in case of performAction().
Is it correct way ? Any other suggestion will be most welcomed?
Question 1: Is my understanding correct or not of this Section? If yes
then what is the way to deal with this part ?
Yes, that seems to be correct. Just because hydra-java decided to creates classes doesn't mean you have to do the same in your implementation though. I would rather write a mapper and annotate an internal class that can then stay stable (you need to update the mapping instead). Your GenericResource approach also looks good btw.
Problem 1: It is not necessary that every propertyName is mapped to
myvocab:propertyName. Some may be mapped to some other vocab eg:
hydra:propertyName, schema:propertyName, rdfs:propertyName,
newVocab:propertyName, etc.
Why don't you store and access the properties with full URLs, i.e., including the vocab? You can of course implement some convenience methods to simplify the work with your vocab.
Problem 2: How to validate whether this propertyName belongs to this
class
Suggestion: Put type field/variable in GenericResource class
JSON-LD's #type in node objects (not in #value objects) corresponds to rdf:type. So simply add it as every other property.
And then check supportedProperty in vocab corresponding to that class.
Please keep in mind that supportedProperty only tells you which properties are known to be supported. It doesn't tell you which aren't. In other words, it is valid to have properties other than the ones listed as supportedProperty on an object/resource.
Ad Q1:
For the flexibility you want, the client has to be prepared for semantic and structural changes.
In HTML that is possible. The server can change the structure of an html form in the way outlined by you, by having a firstName and lastName field rather than just a name field. The client does not break, rather it adjusts its UI, following the new semantics. The trick is that the UI is generated, not fixed.
A client which tries to unmarshal the incoming message into a fixed representation, such as a Java bean, is out of luck, and I do not think there is any solution how you could deserialize into a Java bean and survive a change like yours.
If you do not try to deserialize, but stick to reading and processing the incoming message into a more flexible representation, then you can achieve the kind of evolvability you're after. The client must be able to handle the flexible representation accordingly. It could generate UIs rather than binding data to fixed markup, which means, it makes no assumptions about the semantics and structure of the data. If the client absolutely has to know what a data element means, then the server cannot change the related semantics, it can only add new items with the new semantics while keeping the old ones around.
If there were a way how a server could hand out a new structure with a code-on-demand adapter for existing clients, then the server would gain a lot of evolvability. But I am not aware of any such solutions yet.
Ad Q2:
If your goal is to read an incoming json-ld response into a Jena Model on the client side, please see https://jena.apache.org/documentation/io/rdf-input.html
Model model = ModelFactory.createDefaultModel() ;
String base = null;
model.read(inputStream, base, "JSON-LD");
Thus your client will not break in the sense that it cannot read the incoming response. I think that is what your GenericResource achieves, too. But you could use Jena directly on the client side. Basically, you would avoid unmarshalling into a fixed type.

Play Excel module weird behaviour

I am using Play with Excel module 1.2.3. In a controller, I get a list of Students by calling a method defined in the model - Student:
List<Student> students= Student.findStudents();
findStudents() is defined as:
public static List<Student> findStudents() {
List<Student> list = Student.find("colA != colB").fetch();
return list;
}
then I render the excel file by:
renderExcel("student_report");
Inside the excel template, I have used JXLS. For example:
<jx:forEach items="${students}" var="stu">
${stu.address.name} ${stu.name}
</jx:forEach>
Now, the weird thing happens. stu.name always get displayed fine. However, stu.address.name only get displayed when I have done something like System.out.println(student.address.name) in the code. Otherwise, the cell in the Excel report is blank.
Can anyone explain this?
N.B. Student lazily ref to address
Jxls uses Apache Jexl to process property expressions like stu.address.name. Jexl uses reflection to calculate the object property values.
But reflection and lazy loading do not go along because you work not with a real object but with a proxy object.
When you do System.out.println(student.address.name) the real object is instantiated and the reflection works fine.
Possible solution to the issue is described in this answer Converting Hibernate proxy to real object. Or you just should do eager fetching whenever you need to pass the object to Jxls.

Create new child in Dom

I have created test class that created DOM object ,currently I created some attributes
Hard coded ,for example I have create element name structure and for the structure we have attributes,I have created different class that handle the attribute with constructor .
These is the code from the main method
Properties properties = new Properties(document);
Element Property = properties.getProperty();
Properties.setProperty(Property, "product_id","10", "Pro ID");
Type.appendChild(Property);
Properties properties1 = new Properties(document);
Element Property1 = properties1.getProperty();
Properties.setProperty(Property1, "curr","5", "Curr Code");
Type.appendChild(Property1);
The code in the constructor is
public Properties(Document document) {
Property = document.createElement(PROPERTY);
}
As you can see for create new property I have created element property and property1 etc hard coded,which is problematic since what will happen
If I will have table that with list of properties with there data ,how should I handle it?
I am not sure if the constructor is the right solution and my question is how to do that better ?
Thanks!!!
It depends on what you're trying to do.
If you want to create a DOM so that you can test your classes that build DOM's, then simply hard-coding calls will work just fine. You just have to make sure you use enough combinations of calls to thoroughly test you API.
If, on the other hand, you need to create a DOM so that you can then proceed to test your API's that require some sort of DOM input, you might want to consider simply creating those DOM's in the form of XML documents and then using the org.w3c.dom API's to create the DOM from the XML.

how to automatically copy values from java bean to protobuf message object using java reflection?

Typically I could copy values between two java beans, which have identical property names, using BeanUtils with java reflection e.g. PropertyUtils.setProperty(....)
In protobuf Message, we use the message builder class to set the value. This works but I would rather use reflection to automatically copy properties from the bean to the message as both have identical property names and type.
When I invoke the PropertyUtils.setProperty on the builder object ( got from message.newBuilder()), I get this message.
java.lang.NoSuchMethodException: Property 'testProp' has no setter method in class 'class teststuff.TestBeanProtos$TestBeanMessage$Builder'
How do I automatically copy values from java bean to protobuf message object (and vice-versa) using java reflection?
I hate to answer my question but I cant believe that I am the only one who ran into this problem. Documenting solution here in case other people are also getting started with protobuf and java. Using reflection saves wrting dozens of getter and setters.
Ok , I managed to get it to work using some of example test code shipping with protobuf. This is a very simple use case; typically a message would be a lot more complex. This code does not handle nested messages or repeated messages.
public static void setMessageBuilder(com.google.protobuf.GeneratedMessage.Builder message,Descriptors.Descriptor descriptor,Object srcObject) throws Exception {
String cname = srcObject.getClass().getName();
/*BeanMapper.getSimpleProperties -- this is a warpper method that gets the list of property names*/
List<String> simpleProps = BeanMapper.getSimpleProperties(srcObject.getClass());
Map map = new HashMap();
for (String pName : simpleProps) {
System.out.println(" processing property "+ pName);
Object value= PropertyUtils.getProperty(srcObject, pName);
if(value==null) continue;
Descriptors.FieldDescriptor fd=descriptor.findFieldByName(pName) ;
System.out.println(" property "+ pName+" , found fd :"+ (fd==null ? "nul":"ok"));
message.setField(fd, value);
System.out.println(" property "+ pName+" set ok,");
}
return ;
}
I may be off, but would protostuff help? It has nice extended support for working with other data formats, types. And even if it didn't have direct conversion support, if you go to/from JSON there are many choices for good data binding.
You can go throw all properties getClass().getFields() and make copy using reflection. It will be smt like:
for(Field f : to.getClass().getFields()){
f.set(to, from.getClass().getField(f.getName()).get(from));
}
+ probably you might be use field.setAccessible(true) invocation.
I don't know the size of your project but you may want to try Dozer, a mapper that recursively copies data from one object to another of the same type or between different complex types. Supports implicit and explicit mapping as well. I used it in a big project and worked very well. It could be as simple as
Mapper mapper = new DozerBeanMapper();
DestinationObject destObject = mapper.map(sourceObject, DestinationObject.class);
I've got the same issue, the solution is a little bit tricky.
Please use
MethodUtils.invokeMethod
instead.
where the method name is "setXXX".

JSF: navigation

I have to warn you: the question may be rather silly, but I can't seem to wrap my head around it right now.
I have two managed beans, let's say A and B:
class A
{
private Date d8; // ...getters & setters
public String search()
{
// search by d8
}
}
class B
{
private Date d9; //...getters & setters
public String insert()
{
// insert a new item for date d9
}
}
and then I have two JSP pages, pageA.jsp (the search page) and pageB.jsp (the input page).
What I would like to do is placing a commandbutton in pageB so to open the search page pageA passing the parameter d9 somehow, or navigating to pageA directly after b.insert(). What I would like to do is showing the search result after the insertion.
Maybe it's just that I can't see the clear, simple solution, but I'd like to know what the best practice might be here, also...
I though of these possible solutions:
including **A** in **B** and linking the command button with **b.a.search**
passing **d9** as a **hiddenInput** and adding a new method **searchFromB** in **A** (ugly!)
collapsing the two beans into one
JSF 1.1/1.2 raw doesn't provide an easy way to do this. Seam/Spring both have ways around this and there are a couple of things you can do. JSF 2 should also have solutions to this once it is released.
Probably the easiest and most expedient would be to collapse the two beans into one and make it session scoped. The worry, of course, is that this bean will not get removed and stay in session until the session times out. Yay Memory leaks!
The other solution would be to pass the date on as a GET parameter. For instance, you action method could call the
FacesContext.getCurrentInstance().getExternalContext().redirect("pageB?d9=" + convertDateToLong(d9));
and then get the parameter on the other side.
You should configure the navigation flow in faces-config.xml. In ideal scenario you would return a "status" message which would decide the flow. Read more at following link:
http://www.horstmann.com/corejsf/faces-config.html
http://publib.boulder.ibm.com/infocenter/rtnlhelp/v6r0m0/index.jsp?topic=/com.businessobjects.integration.eclipse.doc.devtools/developer/JSF_Walkthrough8.html
As far as passing the values from one page to another is concerned you can use backing beans. More about backing beans here:
http://www.netbeans.org/kb/articles/jAstrologer-intro.html
http://www.coderanch.com/t/214065/JSF/java/backing-beans-vs-managed-beans
Hope i have understood and answered correctly to your question
Way to share values between beans
FacesContext facesContext = FacesContext.getCurrentInstance();
Application app = facesContext.getApplication();
ExpressionFactory elFactory = app.getExpressionFactory();
ELContext elContext = facesContext.getELContext();
ValueExpression valueExp = elFactory.createValueExpression(elContext, expression, Object.class);
return valueExp.getValue(elContext);
In above code "expression" would be something like #{xyzBean.beanProperty}
Since JSF uses singleton instances, you should be able to access the values from other beans. If you find more details on this technique, I am sure you'll get what you are looking for.
Add commandButton action attribute referencing to B'insert method
<h:commandLink action="#{b.insert}" value="insert"/>
In B'insert method,add d9 parameter as request parameter. Then return an arbitrary string from insert method.
FacesContext fc = FacesContext.getCurrentInstance();
fc.getExternalContext().getRequestMap().put("d9", d9);
Then go to faces context and add navigation from B to A with "from-outcome" as the arbitrary String you returned from insert method. But don't add redirect tag to navigation tags as it will destroy the request coming from B and the parameter you added (d9) will be cleared.
<from-outcome>return string of insert method</from-outcome>
<to-view-id>address of A</to-view-id>
Then you might get the "d9" in A class by fetching it from request map at its constructor or in a place where its more appropriate (getters). You might add it into a session scope or place it to a hidden variable if you want to keep track of it later.
in class A, when page is navigated, A should be initialized as it will be referenced.
FacesContext fc = FacesContext.getCurrentInstance();
fc.getExternalContext().getRequestMap().get("d9", d9);
Sorry i cant give full code, as i have no ide at here, its internet machine at work. I could not give details therefore.
In my opinion, the simplest way is 3-rd option - have both query and insert methods in same class. And you can do something like that:
public String query () {
//...
}
public String Insert() {
//insert
return Query(); }
If your classes are managed Beans you can load class A from class B and call A.query() in your insert method at the end. Also class A can have
<managed-bean-scope>session</managed-bean-scope>
parameter in faces-config.xml and it wouldn't be instantiated again when loaded.

Categories