lunes, 2 de octubre de 2017

JEE & JSF16th Part: Creating an abstraction view layer to JSF components and Forms (5/5). Frequent problems

1. ERROR #1: Using a bean that does not exists

In the previos entry we used this facelet file:



 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
<html xmlns="http://www.w3.org/1999/xhtml" 
    xmlns:h="http://java.sun.com/jsf/html" 
    xmlns:f="http://java.sun.com/jsf/core" 
    xmlns:p="http://primefaces.org/ui">  
    <h:head>  
    </h:head>  
      
    <h:body>  
       <h:form>  
           <h3>
             This file is for testing Primafaces using a Bean (TestComponentBean)
           </h3>  
           <h2>
             In form/form-prova01.json a value property is set to a Bean-Property. and it is assigned programmatically in TestComponentBean
           </h2> 
           <h:panelGroup binding="#{testComponentBean.pg}" /> 
           <p:commandButton value="Submit"/>
       </h:form>
    </h:body>  
</html>

If we create a new facelets file called testpage03-Problems.xhtml and change the red expresion for this one (using a bean that does not exist)

<h:panelGroup binding="#{nonExistingBean.pg}" />



We get a Target Unreachable error:

SEVERE: Servlet.service() for servlet [FacesServlet] in context with path [/JSFv01] threw exception [/pages/testpage03-Problems.xhtml @16,60 binding="#{nonExistingBean.pg}": Target Unreachable, identifier 'nonExistingBean' resolved to null] with root cause
javax.el.PropertyNotFoundException: Target Unreachable, identifier 'nonExistingBean' resolved to null




2. ERROR #2: Using a unexisting property from a bean

 Now we change to 

<h:panelGroup binding="#{testComponentBean.noExistingProperty}" />

We get a Property nor found error:
SEVERE: Servlet.service() for servlet [FacesServlet] in context with
path [/JSFv01] threw exception [/pages/testpage03-Problems.xhtml 
@16,78 binding="#{testComponentBean.noExistingProperty}": 
Property 'noExistingProperty' not found on type 
org.ximodante.jsf.component.test.TestComponentBean] with root cause
javax.el.PropertyNotFoundException: Property 'noExistingProperty' 
not found on type org.ximodante.jsf.component.test.TestComponentBean

JEE & JSF15th Part: Creating an abstraction view layer to JSF components and Forms (4/5). Component builder and form definition

0. Introduction

Once we have loaded all components and attributes catalog by means of a Singleton class in the previous entry, it is interesting to have a class that reads a json file that describes how components are displayed in a fom and creates all components.

Now we need an new entry to pom.xml referencing to Apache Commons BeanUtils that helps us to instantiate and assign value to an object programmatically.

1. Pom.xml file


  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>org.ximodante.jsf</groupId>
  <artifactId>JSFv01</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>war</packaging>
  <name>JSFv01</name>
  <description>JSF 2.2 &amp; CDI</description>
 
  <properties>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    <failOnMissingWebXml>false</failOnMissingWebXml>
  </properties>
  
  <dependencies>
     
    <!-- Servlet 3.1 -->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.1.0</version>
      <scope>provided</scope>
    </dependency>
    
    <!-- To solve Tomcat problem : java.lang.ClassNotFoundException: javax.servlet.jsp.jstl.core.Config -->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>jstl</artifactId>
      <version>1.2</version>
    </dependency>
    
    
    <!--  JSF 2.2 API -->
    <dependency>
     <groupId>com.sun.faces</groupId>
     <artifactId>jsf-api</artifactId>
     <version>2.2.14</version>
    </dependency>

    <!--  JSF 2.2 Implementation -->
    <dependency>
     <groupId>com.sun.faces</groupId>
     <artifactId>jsf-impl</artifactId>
     <version>2.2.14</version>
    </dependency>

    <!--  Primefaces -->
    <dependency>
     <groupId>org.primefaces</groupId>
     <artifactId>primefaces</artifactId>
     <version>6.1</version>
    </dependency>

    <!--  Primefaces Themes -->
    <dependency>
     <groupId>org.primefaces.extensions</groupId>
     <artifactId>all-themes</artifactId>
     <version>1.0.8</version>
     <type>pom</type>
    </dependency>
    
    <!-- Weld CDI for Tomcat (does not fulfill all capabilities !!!) -->
    <dependency>
      <groupId>org.jboss.weld.servlet</groupId>
      <artifactId>weld-servlet-shaded</artifactId>
      <version>3.0.0.Final</version>
    </dependency>
    
    <!-- Validation API Optional -->
    <dependency>
      <groupId>javax.validation</groupId>
      <artifactId>validation-api</artifactId>
      <version>2.0.0.CR3</version>
    </dependency>
        
    <!-- Hibernate Bean Validator Optional -->
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-validator</artifactId>
      <version>5.4.1.Final</version>
    </dependency>
    
    <!--  Lombok for setters and getters -->
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.16.18</version>
    </dependency>
    
    <!-- JSON -->
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.9.0</version>
    </dependency>

    <!-- Apache Commons Utils -->
    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-lang3</artifactId>
      <version>3.6</version>
    </dependency>
    
    <!-- Apache Commons Text -->
    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-text</artifactId>
      <version>1.1</version>
    </dependency>
    
    <!--  Apache commons Beanutils for conversion of types-->
    <!-- https://mvnrepository.com/artifact/commons-beanutils/commons-beanutils -->
    <dependency>
      <groupId>commons-beanutils</groupId>
      <artifactId>commons-beanutils</artifactId>
      <version>1.9.3</version>
    </dependency>
    
    
    <!--  JPA Hibernate -->
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-core</artifactId>
      <version>5.2.10.Final</version>
    </dependency>
    
    <!--  PostgreSQL -->
    <!-- https://mvnrepository.com/artifact/org.postgresql/postgresql -->
    <dependency>
      <groupId>org.postgresql</groupId>
      <artifactId>postgresql</artifactId>
      <version>9.4.1212</version>
    </dependency>
    
    
    <!-- Primeface Extensions -->
    <!-- https://mvnrepository.com/artifact/org.primefaces.extensions/primefaces-extensions -->
    <dependency>
      <groupId>org.primefaces.extensions</groupId>
      <artifactId>primefaces-extensions</artifactId>
      <version>6.1.1</version>
    </dependency>
    
    <!--  for evaluating EL expressions -->
    <!-- https://mvnrepository.com/artifact/javax.el/javax.el-api 16/9/2017-->
    <dependency>
      <groupId>javax.el</groupId>
      <artifactId>javax.el-api</artifactId>
      <version>3.0.1-b04</version>
    </dependency>

  </dependencies> 
  
</project>

2. Form definition file in json

Let's create a sample form definition file in JSON in the src/main/resources/forms. We can have as many forms descriptions files a needed. In this case, the name is form-01-test.json


1
2
3
4
5
6
{ "name" : "main", "type" : "h_HtmlPanelGroup", "layout": "block" ,"children": 
  [ { "name": "Full Name"  , "type": "p_InputText" ,"value":"#{testComponentBean.fullName}" }, 
    { "name": "Color"      , "type": "p_InputText" ,"value":"Red"                    , "disabled":"true"},
    { "name": "Comment"    , "type": "p_InputText" ,"value":"This is a simple example" }
  ]   
}  

As we can see 4 components are defined: There is a container panel that includes 3 input text components.

Notes:
  1. The prefix h_ is js standard Mojarra library and the p_ is for Primefaces (in type parameter).
  2. There is a component that accepts and expresion (#
    {testComponentBean.fullName}
    )

3. ComponentBuilder class

In this class, we will make extense use of Reflection and  BeanUtils. It creates components programmatically and assigns properties distinguishing between string constants and EL expressions and actions.

This component builder retrieves information either from a JSON file or a Map structure.

Here is the code. Note the huge quantity of exceptions that can be thrown

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
package org.ximodante.jsf.component;

import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;

import javax.el.MethodExpression;
import javax.el.ValueExpression;
import javax.enterprise.context.ApplicationScoped;
import javax.faces.component.UIComponent;
import javax.inject.Inject;
import javax.inject.Named;

import org.apache.commons.beanutils.ConvertUtils;
import org.ximodante.utils.jsf.JSFUtils;
import org.ximodante.utils.json.JsonUtils;
import org.ximodante.utils.string.StrUtils;

import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.sun.faces.util.ReflectionUtils;

@Named
@ApplicationScoped
/**
 * JSF component builder from information stored in :
 *   A Java Map or 
 *   A JSON File as a map structure
 *   
 * @author Ximo Dante
 *
 */
public class ComponentBuilder implements Serializable {
 
 private static final long serialVersionUID = 1L;
 
 @Inject 
 private ComponentParams cParams;
 
 /**
  * Read a form.json
  * @param map
  * @param parent
  * @param level
  * @throws ComponentJsfException
  * @throws ClassNotFoundException
  * @throws NoSuchMethodException
  * @throws SecurityException
  * @throws InstantiationException
  * @throws IllegalAccessException
  * @throws IllegalArgumentException
  * @throws InvocationTargetException
  */
 
 public void getComponentFromMap(Map<String,Object> map, UIComponent parent, int level) throws ComponentJsfException, ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
  
  String type=null;
  try {
   type=((String)map.get("type")).trim();
  } catch ( NullPointerException e) {
   throw new ComponentJsfException("Attribute \"type\" not Found in json form file");
  }
  //System.out.println("type="+type);
  
  String qname=null;
  try {
   qname=((String)cParams.getAllComponents().get(type).getQclass()).trim();
  } catch ( NullPointerException e) {
   throw new ComponentJsfException("Component type " + type + " not Found in component-types.json file");
  }
  //System.out.println("qname="+qname);
  
  Class<?> componentClass = ReflectionUtils.lookupClass(qname);
  //System.out.println("componentClass="+qname);
  
  Constructor<?> ctor = componentClass.getConstructor();
  //System.out.println("constructor");
  
  UIComponent component = (UIComponent) ctor.newInstance();
  //System.out.println(component.toString());
  
  
  
  for(String s: map.keySet()) {
   ComponentAttribute cAtt=cParams.getAllAttributes().get(s);
   if (cAtt==null) throw new ComponentJsfException("Component attribute " + s + " not Found in component-attributes.json file");
   
   Object value=map.get(s);
   
   switch (cAtt.getType()) {
    case ARRAY:
     if (s.equals("children")) { 
      for (Map <String,Object> cmap: (List<Map<String,Object>>) map.get(s)) {
       System.out.println("child definition......");
       getComponentFromMap(cmap, component, level+1);
      } 
     } else  throw new ComponentJsfException("Component attribute " + s + " of ARRAY type has unknow treatment in ComponentBuilder class");
     break;
   
    case PROPERTY:
     // If an EL Expression is used, it should be set by setValueExpression method 
     if (value.toString().trim().startsWith("#{")) {
            
      // setValueExpresion has two arguments:
      //  1. propertyName that is a string (in this case is "value"
      //  2. ValueExpression ( in this case "#{panelGroupBean.nom}" in test case)
     
      String methodName="setValueExpression";
      ValueExpression vExp=JSFUtils.createValueExpression(value.toString());
      
      Method method = componentClass.getMethod(methodName, String.class, ValueExpression.class);
      method.invoke(component, cAtt.getName(),vExp);
      System.out.println("Invoked Method Name="+ methodName + "with param type:" + cAtt.getPclass() + " param="  + map.get(s) );
      
     // a normal property assignment
     } else {
      
      // Class of the parameter
      Class<?> pclass=  ReflectionUtils.lookupClass(cAtt.getPclass());
      
      // Method of pattern "setPropertyName" 
      String methodName="set"+ StrUtils.CapitalizeFirst(cAtt.getName());
      
      Method method = componentClass.getMethod(methodName, pclass);
      //System.out.println("METODO: " + method.getName() + "  CLASS: "+pclass.getName() + "  VALUE:" + value);
      method.invoke(component, ConvertUtils.convert(value, pclass));
      //System.out.println("Invoked Method Name="+ methodName + "with param type:" + cAtt.getPclass() + " param="  + map.get(s) );
     }
     break;
     
    case ACTION:
     // Class of the parameter
     Class<?> pclass=  ReflectionUtils.lookupClass(cAtt.getPclass());
     
     // setValueExpresion has two arguments:
     //  1. propertyName that is a string (in this case is "value"
     //  2. ValueExpression ( in this case "#{panelGroupBean.nom}" in test case)
    
     String methodName="setActionExpression";
     MethodExpression vExp=JSFUtils.createMethodExpression(value.toString(), String.class, new Class<?>[]{});
     
     Method method = componentClass.getMethod(methodName, ValueExpression.class);
     method.invoke(component, cAtt.getName(),vExp);
     //System.out.println("Invoked Method Name="+ methodName + "with param type:" + cAtt.getPclass() + " param="  + map.get(s) ); 
     break;
   }
    
  }
  parent.getChildren().add(component);
  
 }
 
 /**
  * Gets a component from information obtained in a json file
  * @param isRelativeToResourceFolder
  * @param fileName
  * @param component
  * @throws JsonParseException
  * @throws JsonMappingException
  * @throws IOException
  * @throws ClassNotFoundException
  * @throws NoSuchMethodException
  * @throws SecurityException
  * @throws InstantiationException
  * @throws IllegalAccessException
  * @throws IllegalArgumentException
  * @throws InvocationTargetException
  * @throws ComponentJsfException
  */
 public void getComponentFromJsonFile(boolean isRelativeToResourceFolder, String fileName, UIComponent component) throws JsonParseException, JsonMappingException, IOException, ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, ComponentJsfException {
  Map<String,Object>mp= JsonUtils.readMap(isRelativeToResourceFolder, fileName);
  getComponentFromMap(mp, component, 0);
 }
 
}

4. Testing

This is the simple facelets file for testing all previous stuff (testpage02-PanelGroup.xhtml in the webapp/pages folder)


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
<html xmlns="http://www.w3.org/1999/xhtml" 
    xmlns:h="http://java.sun.com/jsf/html" 
    xmlns:f="http://java.sun.com/jsf/core" 
    xmlns:p="http://primefaces.org/ui">  
    <h:head>  
    </h:head>  
      
    <h:body>  
       <h:form>  
           <h3>
             This file is for testing Primafaces using a Bean (TestComponentBean)
           </h3>  
           <h2>
             In form/form-01-test1.json a value property is set to a Bean-Property. and it is assigned programmatically in TestComponentBean
           </h2> 
           <h:panelGroup binding="#{testComponentBean.pg}" /> 
           <p:commandButton value="Submit"/>
       </h:form>
    </h:body>  
</html>

And this bean will be used for binding the HtmlPanelGroup in the previous facet file 


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
package org.ximodante.jsf.component.test;

import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;

import javax.annotation.PostConstruct;
import javax.faces.component.html.HtmlPanelGroup;
import javax.faces.view.ViewScoped;
import javax.inject.Inject;
import javax.inject.Named;

import org.ximodante.jsf.component.ComponentBuilder;
import org.ximodante.jsf.component.ComponentJsfException;

import lombok.Getter;
import lombok.Setter;

/**
 * Uses xhtml page
 * @author Ximo Dante
 *
 */

@Named
@ViewScoped
public class TestComponentBean implements Serializable{
 private static final long serialVersionUID = 1L;

 @Getter @Setter private HtmlPanelGroup pg = new HtmlPanelGroup();
 @Getter @Setter private String fullName="Ximo Dante";
 
 
 @Inject
 ComponentBuilder cp;
 
 @PostConstruct
    public void init() {
  pg.setLayout("block");
   
  
  // Programmatically create child component by reflection from a json file
  try {
   cp.getComponentFromJsonFile(true, "forms/form-01-test.json", pg);
  } catch (ClassNotFoundException | NoSuchMethodException | SecurityException | InstantiationException
    | IllegalAccessException | IllegalArgumentException | InvocationTargetException | IOException
    | ComponentJsfException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
  
 }
}

If we right-click on the xhtml file and run on server we get this screen


Obviously, this is an ugly page but it is really important to note that it shows everything we have enclosed in the jsf file!

JEE & JSF14th Part: Creating an abstraction view layer to JSF components and Forms (3/5). Retrieving configuration from resources

0. Introduction


To retrieve information stored in resources files, if the amount of memory to be used is not very high, it is interesting considering a Singleton component.

In a CDI scenario it can be achieved by means of @ApplicationScoped annotation.

1. ComponentParams class


This class populates all components and attribute types. The JsonUtils class is shown in an elder entry.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
package org.ximodante.jsf.component;

import java.io.IOException;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import javax.annotation.PostConstruct;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Named;

import org.ximodante.utils.json.JsonUtils;

import com.fasterxml.jackson.core.type.TypeReference;


import lombok.Getter;


@Named
@ApplicationScoped

/**
 * Loads the components and attribute catalog from resources/config folder
 * It is a Singleton object and stores a map of components and resources.
 * 
 * @author Ximo Dante
 *
 */
public class ComponentParams implements Serializable{
 
 private static final long serialVersionUID = 1L;
 
 @Getter private Map<String,ComponentAttribute> allAttributes;
 @Getter private Map<String,ComponentType> allComponents;
 
 
 @PostConstruct
 public void init()  {
  System.out.println("Initializing ComponentParams...");
  try {
   // 1. populate allAttributes
   List<ComponentAttribute> myListA=
     JsonUtils.readObject(true, "config/component-attributes.json",
       new TypeReference<List<ComponentAttribute>>() { } );
   allAttributes = myListA.stream()
     .collect(Collectors
     .toMap(x -> x.getName(), x -> x));
   
   
   // 2. Populate allComponents
   List<ComponentType> myListT=
     JsonUtils.readObject(true, "config/component-types.json",
       new TypeReference<List<ComponentType>>() { } );
   allComponents = myListT.stream()
     .collect(Collectors
     .toMap(x -> x.getName(), x -> x));
   
  } catch (IOException e) {
   e.printStackTrace();
  }
 }
 
 /*
 public static void main(String[] args) {
  ComponentParams cp= new ComponentParams();
  
  System.out.println("AllAttributes------");
  for (String s:cp.getAllAttributes().keySet()) {
   System.out.println(s + "=" + cp.getAllAttributes().get(s).toString());
  }
  
  System.out.println("AllComponents------");
  for (String s:cp.getAllComponents().keySet()) {
   System.out.println(s + "=" + cp.getAllComponents().get(s).toString());
  }
  
 } 
 */
 
}

JEE & JSF13th Part: Creating an abstraction view layer to JSF components and Forms (2/5). Storing configuration

0. Introduction


It is important to have a catalogue of all resources needed to control components and attributes that can be used. It is important for detecting uncatalogued elements used in the application.

As JSON format is relatively easy to be read, it is our candidate format.

The folder to store this catalogues is src/main/resources/config

For now, only a small number of elements are catalogued, but this is an alive catalogue, so as more components are needed, they should be enclosed in the catalogue.

1. Attributes catalogue

The name of this file is component-attributes.json and stores elements of ComponentAttribute class (that was seen in the last post) a sample is


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
[
 {"name":"name"       , "type":"OTHER"                                   , "comment":"name"},
 {"name":"type"       , "type":"OTHER"                                   , "comment":"type of the component with its prefix. (h_HtmlPanelGroup, p_InputText)"},
 
 {"name":"id"         , "type":"PROPERTY" , "pclass":"java.lang.String"  , "comment":"id"},
 {"name":"value"      , "type":"PROPERTY" , "pclass":"java.lang.Object"  , "comment":"Only for primefaces"},
 {"name":"styleClass" , "type":"PROPERTY" , "pclass":"java.lang.String"  , "comment":"To set style"},
 {"name":"class"      , "type":"PROPERTY" , "pclass":"java.lang.String"  , "comment":"To set style"},
 {"name":"disabled"   , "type":"PROPERTY" , "pclass":"boolean"           , "comment":"To disable input. Carefull with primitive types!"},
 {"name":"icon"       , "type":"PROPERTY" , "pclass":"java.lang.String"  , "comment":"To attach an icon"},
 
 {"name":"for"        , "type":"PROPERTY" , "pclass":"java.lang.String"  , "comment":"Used in labels to be attavhed to input fields"},
 
 {"name":"layout"     , "type":"PROPERTY" , "pclass":"java.lang.String"  , "comment":"If block and applied to an HtmlPanelGroups quets a div, else a span"},
 
 {"name":"children"   , "type":"ARRAY"                                   , "comment":"A collection of child component definitions"}
]


2. Components catalogue

The name of this file is component-types.json and stores elements of Component class (that was seen in the last post) a sample is


1
2
3
4
5
6
7
8
[{"name" :"p_InputText"     , "container":"false", "qclass":"org.primefaces.component.inputtext.InputText"       , "comment":"input text" },
 {"name" :"p_OutputLabel"   , "container":"false", "qclass":"org.primefaces.component.outputlabel.OutputLabel"   , "comment":"label "},
 {"name" :"p_AutoComplete"  , "container":"false", "qclass":"org.primefaces.component.autocomplete.AutoComplete" , "comment" : "input text with autocomplete"},
 
 
 {"name" :"h_HtmlPanelGroup", "container":"true" , "qclass":"javax.faces.component.html.HtmlPanelGroup"          , "comment" : "div: if layout=block; span if no layout attribute"}
    
]

Note the prefixes (p_, h_,..) in the name of components so that we can differentiate the libraries they belong to.


3. Components libraries catalogue


The name of this file is component-libraries.json and stores the prefixes used in the name of components in the component catalogue. 

1
2
3
4
5
6
[{"prefix" : "h",  "name" : "Mojarra JSF or Facelets"      },
 {"prefix" : "ui", "name" : "Used in <ui:repeat>"          },
 {"prefix" : "cc", "name" : "Used in composite components>"},
 {"prefix" : "p",  "name" : "PrimeFaces"                   },
 {"prefix" : "pe", "name" : "PrimeFaces Extensions"        }
]

lunes, 18 de septiembre de 2017

JEE & JSF12th Part: Creating an abstraction view layer to JSF components and Forms (1/5). Description classes

0. Introduction

So far, we have learned to create JSF components programmatically with the desired properties and actions.

Imagine you want to migrate your application to another framework.

A way to achieve this is using classes that :

  1. Hold or retrieve the necessary information to describe the desired forms or components.
  2. Create the components programmatically (Builders)
Let's create a new java package called org.ximodante.jsf.component

In this entry, we will focus on the structure and classes that define components, attributes, actions and so on. 

1. AttributeType structure

This structure defines the type of an element of a component (I have decided it to be one of the following:

  1. property
  2. action
  3. array (of child components)
  4. other

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
package org.ximodante.jsf.component;

/**
 * 
 * @author Ximo Dante
 * A compompent attribute can be :
 *   1. property that can be accessed by a getter/setter
 *   2. action from a button, etc
 *   3. array like children, ...
 *   4. other
 *
 */
public enum AttributeType {
 PROPERTY,
 ACTION,
 ARRAY,
 OTHER
}


2. ComponentAttribute class

It is intended to have a catalogue of all attributes so that we can detect if a not catalogued attribute is proposed to be employed.

The attribute has a name and belongs to a defined class. Here is the source:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package org.ximodante.jsf.component;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

/**
 * 
 * @author ximo Dante
 * 16/9/2017
 * name: name of attribute
 * setter : if the setter is buid by set + Name
 * pclass : class of the parameter or argument of setter
 * comment: comments
 */

@NoArgsConstructor @AllArgsConstructor
public class ComponentAttribute {
 @Getter @Setter private String name;
 @Getter @Setter private AttributeType type=AttributeType.PROPERTY;
 @Getter @Setter private String pclass;
 @Getter @Setter private String comment="This is a comment";
}

3. ComponentType class

It is intended to have a catalogue of all component types so that we can detect if a not catalogued component is intended to be used.

As we know there are some jsf components from different libraries that share the same name. So let's append a letter prefix and an underscore to the name to distinguish them.

For instance, the name "h_HtmlPanelGroup" is used to represent a panel of the class "javax.faces.component.html.HtmlPanelGroup"

The component type class is:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package org.ximodante.jsf.component;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

/**
 * 
 * @author ximo Dante
 * 15/9/2017
 * name: prefix + "_" + name of component
 * container : if can have children
 * qclass : qualified class
 * comment: comments
 */
@NoArgsConstructor @AllArgsConstructor
public class ComponentType {
 @Getter @Setter private String name;
 @Getter @Setter private boolean container;
 @Getter @Setter private String qclass;
 @Getter @Setter private String comment;
 
} 

4. Exceptions

Let's create an Exception class for this package:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
package org.ximodante.jsf.component;

/**
 * Exception for component building
 * @author Ximo Dante
 *
 */
public class ComponentJsfException extends Exception{
 private static final long serialVersionUID = 1L;

 public ComponentJsfException(String message) { 
  super(message); 
 }
 

}

JEE & JSF16th Part: Creating an abstraction view layer to JSF components and Forms (5/5). Frequent problems

1. ERROR #1: Using a bean that does not exists In the previos entry we used this facelet file: 1 2 3 4 5 6 7 8 9 10 11 1...