martes, 8 de agosto de 2017

JEE & JSF 4th Part: A Primefaces programmatic menu

0. Introduction

To show formatted text Hilite is used

Programmatic elements such as menus, form ... have pros and cons. Let us see the cons:

  1. Rather difficult to code
  2. More time invested in programming at first
  3. Prone to find unexpected errors as the system is not usually for this purpose.
  4. Usually, the project does not take the correct direction and should be abandoned.
  5. Requires a higher level of knowledge and skills.
  6. Testing should be more exigent as an error propagates to all modules. An error in a builder class creates errors in all elements. 
  7. Version control is more exigent too.
But if we cross this desert, the promised land is available to us. The pros are:
  1. The programmatic elements are all very similar, so the user once knows how to use an element, gets used to the rest of elements.
  2. A meta language is created so that not so qualified programmers can build elements.
  3. Productivity increases.
  4. Quicker delivery.
So not everything in the garden is rosy.

The inspiration of this is in Primefaces Showcase

The goals of this post are:

  1. Define a pom.xml file with needed dependencies.
  2. Design our page (xhtml file) using Layout panel
  3. Define a bean to map the structure of a menu (MenuBean)
  4. Define the menu structure in a json file
  5. Define a custom exception
  6. Define beans for handling some actions (MenuActionExample)
  7. Define a class to read this file and assign values to MenuBean.

1. Pom.xml

Two new dependencies are needed, one for json files (jackson) and the other one for using standard utilities in Java (Apache Commons)

Finally, the updated pom.xml file 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
 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
<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>
  </dependencies> 
  
  
</project>


2. Using Layout panel to design out page

The primefaces Layout panel seems a good choice to dispose of our elements. The menu will be displayed on the Left (West) panel. The center panel is for the content and the rest of the panels are omitted.

Layout from Primefaces showcase
To follow the primefaces example a "growl" element is included. The file is

tutorial01-menu.xhtml



 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
<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>
        <p:layout fullPage="true">
 
        
          <p:layoutUnit position="west" size="200" header="Left" resizable="true" closable="true" collapsible="true" effect="drop">
          
          
          
            <p:growl id="messages" showDetail="false"/>
              
            <p:panelMenu id="menu01" model="#{menuBean.model}"/>
 
        
          </p:layoutUnit>
        
          <p:layoutUnit position="center">
            Content panel    
            <p:commandButton value="Update Menu02" id="menuupd02" actionListener="#{menuBean.readMenu(true,'config/menu02.json')}" icon="ui-icon-disk" update="menu01"/>       
            <p:commandButton value="Update Menu"   id="menuupd"   actionListener="#{menuBean.readMenu(true,'config/menu.json')}"   icon="ui-icon-disk" update="menu01"/>       
          </p:layoutUnit>
 
        </p:layout>
     </h:form>
      
   
  </h:body>  
</html>

Note the line

  <p:panelMenu model="#{menuBean.model}"/>

for describing the menu.

Our design is this one

+-----------------+---------------------------+
|                 |          Botton1 Button2  |
|  MENU           |   CONTENT                 |
|                 |                           |
|  (west panel)   | (center panel)            |
|                 |                           |
|                 |                           |
+-----------------+---------------------------+

Two buttons have been included to show how to change the contents of the menu programmatically

  <p:commandButton value="Update Menu02" id="menuupd02" actionListener="#{menuBean.readMenu(true,'config/menu02.json')}" icon="ui-icon-disk" update="menu01"/>       
  <p:commandButton value="Update Menu"   id="menuupd"   actionListener="#{menuBean.readMenu(true,'config/menu.json')}"   icon="ui-icon-disk" update="menu01"/>       
         
Each button aims to a different file (menu.json and menu02.json) in the config folder where the structure of the menu is defined.

When clicking a button the menu should change

3. MenuBean class

The source code of this 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
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
package org.ximodante.jsf.menu;

import java.io.IOException;
import java.io.Serializable;
import javax.annotation.PostConstruct;
/********************************************************
 * CAREFUL USE: import javax.faces.view.ViewScoped 
 *  DO NOT USE: import javax.bean.view.ViewScoped
 ********************************************************/
import javax.faces.view.ViewScoped;
import javax.inject.Named;

import org.primefaces.model.menu.DefaultMenuModel;
import org.primefaces.model.menu.MenuModel;
import lombok.Getter;


/**
 * Implements the menui structure as a bean
 * If a bean does not imnnplements Serializable, Tomcat crashes
 * MenuModel is the attribute to implement the men structure
 * initialMenuConfig is the name of the file to read the initial menu structure
 * 
 * @author Ximo Dante
 *
 */
@Named
@ViewScoped
public class MenuBean implements Serializable {
 
 private static final long serialVersionUID = 1L;
 private static final String initialMenuConfig="config/menu.json";
 
 @Getter  
 private MenuModel menuModel;
  
    @PostConstruct
    public void init() {
        
     readMenu(true, initialMenuConfig);
     
    }
 
    /**
     * Reads the menu structure from a Json file using a class for that purpose.
     * @param isRelativeToResourceFolder (if relative Paths are used to access the config file)
     * @param fileName (Name of the file where configuration is stored)
     */
    public void readMenu(boolean isRelativeToResourceFolder, String fileName) {
     
     menuModel = new DefaultMenuModel();
     System.out.println("MenuBean.readMenu(" + isRelativeToResourceFolder + "," + fileName + ")" );
     try {
         new MenuBeanReaderJSON().readMenu(true, fileName, menuModel);
 } catch (IOException | MenuJsfException e) {
   
         e.printStackTrace();
 }
    }
}    
     
    

The key of this bean is the MenuModel that references the jsf menu to be displayed.

The @PostConstruct annotation is used to initialize the component. (In this case reads the config file from "config/menu.json" in the "src/main/resources" folder and creates the menu structure.

The readMenu method makes use of a helper class (MenuBeanReaderJSON)to read the configuration from a Json File. It is important to delegate to another class as the menu configuration may be saved in other formats (in the data base or other media) and this helper class is a candidate to be injected by Weld CDI.


4. The reader class MenuBeanReaderJSON

The objectives of this class are:

  1. Read the content of the menu description file
  2. Load this content into a handy map structure
  3. Fill the MenuModel element using the map



  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
package org.ximodante.jsf.menu;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Map;

import org.primefaces.model.menu.DefaultMenuItem;
import org.primefaces.model.menu.DefaultSubMenu;
import org.primefaces.model.menu.MenuModel;
import org.ximodante.utils.json.JsonUtils;
import org.ximodante.utils.menuOLD.MenuItemType;

import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;

public class MenuBeanReaderJSON {
 
  
 /**
  * Reads the menu from a JSON object as a Map
  * @param isRelativeToResourceFolder
  * @param fileName
  * @param menu
  * @throws JsonParseException
  * @throws JsonMappingException
  * @throws IOException
  * @throws MenuJsfException
  */
 public void readMenu (boolean isRelativeToResourceFolder, String fileName, MenuModel menu) 
  throws JsonParseException, JsonMappingException, IOException, MenuJsfException {
  
  Map<String,Object> map=JsonUtils.readMap(true, fileName );
  
  //getMenuFromMap(map, menu.getModel(), null, 0);
  getMenuFromMap(map, menu, null, 0);
    
 }
 
 /**
  * Transform a Map structure from a JSon object into a MenuModel o Submenu.
  * One and only one of either menu or submenu is null 
  * @param map
  * @param menu
  * @param submenu
  * @throws MenuJsfException
  */
 @SuppressWarnings("unchecked")
 private static void getMenuFromMap(Map<String,Object> map, MenuModel menu, DefaultSubMenu submenu, int level) throws MenuJsfException {
 
  String name = null; String command = null; String icon = null;
  String url  = null; String update  = null; Boolean ajax = null;
  MenuItemType type = MenuItemType.ITEM; // by default
  ArrayList<Map<String,Object>> list = null; 
  
  // 0. Extract information from Map
  for(String s: map.keySet()) {
   switch (s) {
    case "name"     : name   =(String) map.get("name"   ); break;
    case "command"  : command=(String) map.get("command"); break;
    case "icon"     : icon   =(String) map.get("icon")   ; break;
    case "url"      : url    =(String) map.get("url")    ; break;
    case "update"   : update =(String) map.get("update") ; break;
   
    case "ajax"     : ajax   =Boolean.valueOf((String) map.get("ajax")) ; break;
   
    case "type"     : type   =MenuItemType.valueOf((String) map.get("type")); break;
   
    case "elements" : list   = (ArrayList<Map<String,Object>>) map.get("elements"); break;
          
    default: throw new MenuJsfException ("KEY: " + s + " is not allowed in 'menu.json' (only name, type, comand, icon, url, update, ajax, elements) ");
   } 
  }
  
  // 1. Set default values
  //  The first level is for main menu
  if (level==0) {
   if (name == null) name="main";
   type = MenuItemType.MENU;
  }
  
  //  if elements is not null it is of type submenu/menu
  if (level>0) {
   if (list != null) type = MenuItemType.SUBMENU;
  }
  
  // 2. Detect errors
        if (name == null) throw new MenuJsfException ("No Name in a menu item.");
        
        if(name.trim().compareToIgnoreCase("main")==0) {
         if (type   != MenuItemType.MENU) throw new MenuJsfException ("menuitem.name='main' and it is not MENU type.");
         if (menu   == null)              throw new MenuJsfException ("menuitem.name='main' and MenuModel is NULL.");
         if (submenu!= null)              throw new MenuJsfException ("menuitem.name='main' and DefaultSubMenu is NOT NULL.");
        }
        
        if(name.trim().compareToIgnoreCase("main")!=0) {
         if (type == MenuItemType.MENU) throw new MenuJsfException ("menuitem.name IS NOT 'main' and it is MENU type.");
         //if (menu   != null)            throw new MenuJsfException ("menuitem.name IS NOT 'main' and MenuModel is NOT NULL.");
         //if (submenu== null)            throw new MenuJsfException ("menuitem.name IS NOT 'main' and DefaultSubMenu is NULL.");
        }
        
        if (type   == MenuItemType.MENU || type  == MenuItemType.SUBMENU) {
         if (command!= null)              throw new MenuJsfException (type + " type and 'command' is NOT NULL.");
         if (icon   != null)              throw new MenuJsfException (type + " type and 'icon' is NOT NULL.");
         if (update != null)              throw new MenuJsfException (type + " type and 'icon' is NOT NULL.");
         if (ajax   != null)              throw new MenuJsfException (type + " type and 'ajax' is NOT NULL.");
        }
                
        if (type == MenuItemType.ITEM && list !=null) throw new MenuJsfException ("Only MENU and SUBMENU types can have nested elements"); 
        
        System.out.println(name + ' ' + type);
        // 3.1. MENU (Main)
        if (type == MenuItemType.MENU) {
         System.out.println("Defining Main Menu");
         for (Map<String,Object> mapChild: list) {
          getMenuFromMap(mapChild, menu, null, level +1);
         }
        }
        
        // 3.2 SUBMENU
        if (type == MenuItemType.SUBMENU) {
         DefaultSubMenu sbMnu = new DefaultSubMenu(name);
         
         if (icon   != null) sbMnu.setIcon (icon);
         
         System.out.println("Defining SubMenu->" + name);
         
         for (Map<String,Object> mapChild: list) {
          getMenuFromMap(mapChild, null, sbMnu, level +1);
         }
         
         if (menu !=null) menu.addElement(sbMnu);
         else             submenu.addElement(sbMnu);
         
        }
        
        // 3.3 ITEM
        if (type == MenuItemType.ITEM) {
         
         DefaultMenuItem item = new DefaultMenuItem(name);
         if (url    != null) item.setUrl    (url);
         if (icon   != null) item.setIcon   (icon);
         if (command!= null) item.setCommand(command);
         if (update != null) item.setUpdate (update);
         if (ajax   != null) item.setAjax   (ajax);
         
         System.out.println("Defining item->" + name);
         
         if (menu !=null) menu.addElement(item);
         else             submenu.addElement(item);
        }
        
   
 }
 
 
 /**
 public static void main(String[] args) {
  
  Map<String,Object> map =new HashMap<>();
  map.put("name", "main");
  String pp=(String)map.get("surname");
  System.out.println(pp);
  pp=(String)map.get("name");
  System.out.println(pp);  
  // TODO Auto-generated method stub

 }
    */

}

The map structure tries to emulate the menu structure, so the name of the keys are copied from the xml attributes defined for a Primefaces menu (url, icon, command,update, ajax), other are for internal use (name, type)

To define the node type an enum type is created (MenuItemType)



1
2
3
4
5
6
7
package org.ximodante.jsf.menu;

public enum MenuItemType {
 MENU,
 SUBMENU,
 ITEM;
}



In a future post,  JBoss Weld CDI will enable us to define helper classes to load menu configurations from different media (database, plain text file etc) and we will try to inject the reader helper class to the MenuBean.

Note that Main menu and submenu are distinguished to build the menu tree structure.

Another helper class for managing Json Objects is JsonUtils

JsonUtils also uses FileUtils, let's see these classes.


5. JsonUtils & FileUtils


In this post, the method to read a Json file into a map is used.

JsonUtils:

  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
package org.ximodante.utils.json;


import java.io.IOException;
import java.util.Map;

import org.ximodante.utils.file.FileUtils;

import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;


public class JsonUtils {
  
 /**
  * Reads a json object and transforms it to the desired class 
  * @param fileName Path of the file to read
  * @param clazz Class of the object to get
  * @return Object of that class "clazz"
  * @throws JsonParseException
  * @throws JsonMappingException
  * @throws IOException
  */
 public static <T> T readObject (boolean isRelativeToResourceFolder, String fileName, Class<T> clazz ) throws JsonParseException, JsonMappingException, IOException {
  ObjectMapper mapper = new ObjectMapper();
  return mapper.readValue(FileUtils.getFile(isRelativeToResourceFolder, fileName), clazz);
 }
 
 /**
  * Writes a JSON Object to a file
  * @param isRelativeToResourceFolder
  * @param fileName
  * @param obj
  * @throws JsonGenerationException
  * @throws JsonMappingException
  * @throws IOException
  */
 public static <T> void writeObject (boolean isRelativeToResourceFolder, String fileName, T obj ) throws JsonGenerationException, JsonMappingException, IOException  {
  ObjectMapper mapper = new ObjectMapper();
  mapper.writeValue(FileUtils.getFile(isRelativeToResourceFolder, fileName), obj);//Plain JSON
 }

 /**
  * Reads a JSON Object as a Map
  * @param isRelativeToResourceFolder
  * @param fileName
  * @return
  * @throws JsonParseException
  * @throws JsonMappingException
  * @throws IOException
  */
 public static Map<String,Object> readMap (boolean isRelativeToResourceFolder, String fileName) throws JsonParseException, JsonMappingException, IOException {
  ObjectMapper mapper = new ObjectMapper();
  return mapper.readValue(FileUtils.getFile(isRelativeToResourceFolder, fileName), new TypeReference<Map<String, Object>>() {
  });
 }
 
 
 /**
  * Writes a map to a file as a JSON Object
  * @param isRelativeToResourceFolder
  * @param fileName
  * @param map
  * @throws JsonParseException
  * @throws JsonMappingException
  * @throws IOException
  */
 public static void writeMap (boolean isRelativeToResourceFolder, String fileName, Map<String,Object> map) throws JsonParseException, JsonMappingException, IOException {
  ObjectMapper mapper = new ObjectMapper();
  mapper.writeValue(FileUtils.getFile(isRelativeToResourceFolder, fileName), map);//Plain JSON
  
 }
 
 /**
  * 
  * @param args
  */
 /**
 public static void main(String[] args) {
    
  
  
  
  Prova prova= new Prova();
  System.out.println(prova.camp1);
  System.out.println(prova.camp2);
  Prova prova1;
  try {
   writeObject(true,"prova1.json",prova); //PLAIN JSON; 
   
   //ObjectMapper mapper = new ObjectMapper(); 
            //mapper.writerWithDefaultPrettyPrinter().writeValue(new File("src/main/resources/prova2.json"), prova);//Prettified JSON
   prova1 =readObject(true, "prova.json", prova.getClass());
   System.out.println(prova1 .camp1);
   System.out.println(prova1 .camp2);
        } catch (Exception e) {
            e.printStackTrace();
        }
  
  // TODO Auto-generated method stub

 }
    */
}


FileUtils:



  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
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
package org.ximodante.utils.file;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.stream.Stream;

/**
 * 
 * @author Ximo Dante
 * Reads the content of a file 
 * 
 */
public class FileUtils {
 
 public static final String RESOURCES_FOLDER = "src/main/resources";  
 public static final boolean IS_WEB = true; // If run on server
 
 
 /**
  * Gets a File Object pointing to a Path
  * <ol>
  * <li> If isRelativeToResourceFolder is TRUE,  then PATH= usr/main/resources/ + fileName
  * <li> If isRelativeToResourceFolder is FALSE, then PATH= fileName
  * </ol>
  * 
  * @see    https://www.mkyong.com/java/java-read-a-file-from-resources-folder/
  * @see    http://www.baeldung.com/java-properties
  * 
  * @param  isRelativeToResourceFolder
  * @param  fileName
  * 
  * @return a File Object pointing to a path.
  */
 
 
 private static String relativePath(String fileName) {
  
  String path="";
  if (IS_WEB) {
   /* Mykong https://www.mkyong.com/java/java-read-a-file-from-resources-folder/
   ClassLoader classLoader = new FileUtils().getClass().getClassLoader();
   path = classLoader.getResource("").getFile() + fileName;
   */
   // Baeldung http://www.baeldung.com/java-properties
   path = Thread.currentThread().getContextClassLoader().getResource("").getPath() + fileName;
  } else path= (RESOURCES_FOLDER + '/' + fileName).replaceAll("//", "/");
  System.out.println("PATH=" + path);
  
  
  return path;
 }
 
 public static File getFile(boolean isRelativeToResourceFolder, String fileName) {
  if (isRelativeToResourceFolder) return new File(relativePath(fileName));
  else return new File(fileName);
   
 }

 public static Path getPath(boolean isRelativeToResourceFolder, String fileName) {
  if (isRelativeToResourceFolder) return Paths.get(relativePath(fileName));
  else return Paths.get(fileName);
   
 }
 
 /**
  * This method is only valid for small files as it is high memory consumer
  * @see    http://winterbe.com/posts/2015/03/25/java8-examples-string-number-math-files/
  * @param  isRelativeToResourceFolder if the fileName is relative to usr/main/resources folder
  * @param  fileName
  * @return The content of file as a String
  * @throws IOException
  */
 
 public static String readStrContent(boolean isRelativeToResourceFolder, String fileName) throws IOException {
  File file = getFile(isRelativeToResourceFolder,fileName);
  
        
        if (file.exists()) return new String(Files.readAllBytes(file.toPath()));
        else return "ERROR: File " + fileName + " does not exists!.";
 }
 
 /**
  * Writes a string to a file for creating a small file with the content of a string
  * @param isRelativeToResourceFolder
  * @param fileName
  * @param content
  * @throws IOException
  */
 
 public static void writeStrContent(boolean isRelativeToResourceFolder, String fileName, String content) throws IOException {
  Files.write(getPath(isRelativeToResourceFolder, fileName), content.getBytes());
 }
 
 /**
  * This method is only valid for small files as it is high memory consumer
  * @see    http://winterbe.com/posts/2015/03/25/java8-examples-string-number-math-files/
  * @param  isRelativeToResourceFolder if the fileName is relative to usr/main/resources folder
  * @param  fileName
  * @return The content of file as a List<String>
  * @throws IOException
  */
 
 public static List<String> readLstContent(boolean isRelativeToResourceFolder, String fileName) throws IOException {
  File file = getFile(isRelativeToResourceFolder,fileName);
        
        if (file.exists()) return Files.readAllLines(file.toPath());
        else return null;
 }
 
 /**
  * Writes a List<String> to a file for creating a small file with the content of a List<String>
  * @see    http://winterbe.com/posts/2015/03/25/java8-examples-string-number-math-files/
  * @param isRelativeToResourceFolder
  * @param fileName
  * @param content
  * @throws IOException
  */
 public static void writeLstContent(boolean isRelativeToResourceFolder, String fileName, List<String> content) throws IOException {
  Files.write(getPath(isRelativeToResourceFolder,fileName), content);
 }
 
 /**
  * This method is valid for all files, and returns a Java Stream 
  * @see    http://winterbe.com/posts/2015/03/25/java8-examples-string-number-math-files/
  * @param  isRelativeToResourceFolder if the fileName is relative to usr/main/resources folder
  * @param  fileName
  * @return The content of file as a Stream<String>
  * @throws IOException
  */
 
 public static Stream<String> readStrmContent(boolean isRelativeToResourceFolder, String fileName) throws IOException {
  File file = getFile(isRelativeToResourceFolder,fileName);
        
        if (file.exists()) return Files.lines(file.toPath());
        else return null;
 }
 
 /**
  * Writes a Stream<String> to a file for creating a file with the content of a Stream<String>
  * @see    http://winterbe.com/posts/2015/03/25/java8-examples-string-number-math-files/
  * @param isRelativeToResourceFolder
  * @param fileName
  * @param content
  * @throws IOException
  */
 
 public static void writeStrmContent(boolean isRelativeToResourceFolder, String fileName, Stream<String> content) throws IOException {
  Files.write(getPath(isRelativeToResourceFolder,fileName), (Iterable<String>)content::iterator);
 }
 
 /**
  * This method is valid for all files, and returns a Buffered reader 
  * @see    http://winterbe.com/posts/2015/03/25/java8-examples-string-number-math-files/
  * @param  isRelativeToResourceFolder if the fileName is relative to usr/main/resources folder
  * @param  fileName
  * @return The content of file as a Stream<String>
  * @throws IOException
  */
 
 public static BufferedReader readBufContent(boolean isRelativeToResourceFolder, String fileName) throws IOException {
  File file = getFile(isRelativeToResourceFolder,fileName);
        
        if (file.exists()) return Files.newBufferedReader(file.toPath());
        else return null;
 }
 
 /**
  * Writes a String to a file for creating a file with the content of a String
  * @see    http://winterbe.com/posts/2015/03/25/java8-examples-string-number-math-files/
  * @param isRelativeToResourceFolder
  * @param fileName
  * @param content
  * @throws IOException
  */
 public static void writeBufContent(boolean isRelativeToResourceFolder, String fileName, String content) throws IOException {
  BufferedWriter writer = Files.newBufferedWriter(getPath(isRelativeToResourceFolder,fileName));
  writer.write(content);
  writer.close();
 }
 
 
 
 
 
 /**
  * Examples of using these methods
  * @param args
  */
 /**
 public static void main(String[] args) {
  try {
   
   
   String fileName="config/menu.json";
   System.out.println("Reading file from 'src/main/resources/" + fileName);
   
   System.out.println("\nUSING readStrContent-----------------");
   System.out.println(readStrContent(true, fileName));
   
   System.out.println("\nUSING readLstContent-----------------");
   readLstContent(true, fileName)
     .stream()
     .forEach(System.out::println);
   
   System.out.println("\nUSING readStrmContent-----------------");
   readStrmContent(true, fileName)
     .forEach(System.out::println);
   
   System.out.println("\nUSING readBufContent-----------------");
   readBufContent(true, fileName)
     .lines()
     .forEach(System.out::println);
   
   System.out.println("\n\n===========================================================");
   fileName="kk/writeStrContent.txt";
   System.out.println("\nWriting file to 'src/main/resources/" + fileName);
   System.out.println("USING writeStrContent-----------------");
   writeStrContent(true, fileName,"Writing to "+ fileName);
   
   fileName="kk/writeLstContent.txt";
   System.out.println("\nWriting file to 'src/main/java/" + fileName);
   System.out.println("USING writeLstContent-----------------");
   List<String>lst = Arrays.asList("Writing to "+ fileName, "Writing to "+ fileName);
   writeLstContent(true, fileName,lst);
   
   fileName="kk/writeStrmContent.txt";
   System.out.println("\nWriting file to 'src/main/java/" + fileName);
   System.out.println("USING writeStrmContent-----------------");
   lst = Arrays.asList("Writing to "+ fileName, "Writing to "+ fileName);
   writeStrmContent(true, fileName,lst.stream());
   
   fileName="kk/writeBufContent.txt";
   System.out.println("\nWriting file to 'src/main/java/" + fileName);
   System.out.println("USING writeBufContent-----------------");
   writeBufContent(true, fileName,"Writing to "+ fileName);
   
   
   
  } catch (IOException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
 }
 */

}

6. Json files for configuring the menu


Two Json files are used (in the "usr/main/resources/config" folder)

  • menu.json
  • menu02.json

menu.json is


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{ "name" : "main", "elements": 
  [ { "name": "Dynamic Submenu", "type": "SUBMENU", "elements" :
      [ { "name": "External021", "url": "http://www.primefaces.org", "icon": "ui-icon-home" } ,
        { "name": "External022", "url": "http://www.primefaces.org", "icon": "ui-icon-home" } ,
        { "name": "Dynamic Actions03", "type": "SUBMENU", "elements" :
          [ { "name": "Save03", "command": "#{menuActionExample.save}", "icon": "ui-icon-disk", "update": "messages"  },
            { "name": "Delete03","command": "#{menuActionExample.delete}", "icon": "ui-icon-close", "ajax": "false" },
            { "name": "Dynamic Actions04", "type": "SUBMENU", "elements" :
              [ { "name": "Save04", "command": "#{menuActionExample.save}", "icon": "ui-icon-disk", "update": "messages"  },
                { "name": "Delete04","command": "#{menuActionExample.delete}", "icon": "ui-icon-close", "ajax": "false" } 
              ]   
            } 
          ]   
        }
      ]   
    },
    { "name": "Dynamic Actions", "elements" :
      [ { "name": "Save02", "command": "#{menuActionExample.save}", "icon": "ui-icon-disk", "update": "messages"  },
        { "name": "Delete02","command": "#{menuActionExample.delete}", "icon": "ui-icon-close", "ajax": "false" } 
      ]   
    }
  ]
}

and menu02.json is



 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
{ "name" : "main", "elements": 
  [ { "name": "Submenu A", "type": "SUBMENU", "elements" :
      [ { "name": "ExternalA1", "url": "http://www.primefaces.org", "icon": "ui-icon-home" } ,
        { "name": "ExternalA2", "url": "http://www.primefaces.org", "icon": "ui-icon-home" } 
      ]   
    },
    { "name": "SubMenu B", "elements" :
      [ { "name": "Save B2", "command": "#{menuActionExample.save}", "icon": "ui-icon-disk", "update": "messages"  },
        { "name": "Delete B2","command": "#{menuActionExample.delete}", "icon": "ui-icon-close", "ajax": "false" } 
      ]   
    }
  ]
}  


7. Project Structure


Two snapshots will help to realize the project structure.




8. Executing the template


Right click on the file "tutorial01-menu.xhtml" and select Run As- Run On Server

and it is displayed



and after clicking in "Update Menu02" button


our menu has changed programmatically !


No hay comentarios:

Publicar un comentario

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...