Mostrando entradas con la etiqueta java. Mostrar todas las entradas
Mostrando entradas con la etiqueta java. Mostrar todas las entradas

viernes, 18 de agosto de 2017

JEE & JSF 6th Part: WELD selecting the injected beans programmatically

0. Introduction


To show formatted text Hilite is used

I can not conceive CDI without facilities to inject dependencies into at runtime.


I have seen these interesting related two questions at StackOverflow (the first from several participants and the second from Harald Wellmann)

This is a little tricky post so, let's try to understand the steps followed to achieve this goal:


  1. Create the main interface whose method should be implemented by the candidate classes
  2. Create an annotation (interface) with a parameter to distinguish classes that use this annotation
  3. Create the classes that implement the main interface and are annotated with the previous annotation with different parameters (in this post two classes are created)
  4. Create a tool class that implements the annotation interface used for managing the parameters of the annotation interface (this is the first tricky step)
  5. Use the type Instance<main interface> to retrieve all implementations of the main interface (this is the second tricky step)
  6. Use the method "select" from the Instance<main interface> to retrieve the desired injection class (passing the parameter of the annotation assigned in the class)

As mentioned in the previous post related to creating a menu programmatically our goal is to read the tree structure of the menu from different sources. In that post, this configuration was read from a file that was located in a relative path from src/main/resources folder by means of a class, now, let's create another class to locate and read this file by means of the absolute path of the file. Both classes will implement the same interface.

The way to select one or the other class is by means of a property "menuBeanReaderType" in the file application.properties. The way to retrieve properties was explained in the previous post for accessing properties


1. Creating the main interface


This interface is IMenuBeanReader 


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

import java.io.IOException;
import org.primefaces.model.menu.MenuModel;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;

/**
 * Interface for reading info to build a menu.
 * @author Ximno Dante
 *
 */
public interface IMenuBeanReader {
 public void readMenu  (String source, MenuModel menuModel)
  throws JsonParseException, JsonMappingException, IOException, MenuJsfException;
}

The methid readMenu must be implemented by our classes.


2. The annotation (interface)


The annotation is ImenuBeanReaderType

This annotation has a parameter "type" that should distinguish the classes that implements the main interface (IMenuBeanReader)



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


import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import javax.inject.Qualifier;

/**
 * For distinguishing different classes that implements the IMenuBeanReader interface
 * @author Ximo Dante
 *
 */
@Qualifier @Retention(RUNTIME) @Target({TYPE, METHOD, FIELD, PARAMETER})
public @interface IMenuBeanReaderType {
 
    String type() default "MenuBeanReaderJSONRelativePath";
}

NOTE: As it can be seen, the default value of type is MenuBeanReaderJSONRelativePath that is coincident with the name of a class that we will create later. This coincidence IS NOT NECESSARY the value of type paramenter could have been for instance "option 1".


3. The classes to be injected

Two classes have been created:

  1. MenuBeanReaderJSONRelativePath
  2. MenuBeanReaderJSONAbsolutePath
It is important to see that these two classes:

  • Implement IMenuBeanReader interface
  • Have been annotated with @IMenuBeanReaderType and have different types (for simplicity the name of each class has been used, but not necessarily)
  • Only the first class has the annotation @Default (it is recommended)
  • Annotations @Named and their scopes have been applied.


Let's see MenuBeanReaderJSONRelativePath 


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

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

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Default;
import javax.inject.Named;

import org.primefaces.model.menu.MenuModel;
import org.ximodante.utils.json.JsonUtils;

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

@Named
@ApplicationScoped
@Default
@IMenuBeanReaderType(type="MenuBeanReaderJSONRelativePath")
public class MenuBeanReaderJSONRelativePath implements IMenuBeanReader {
    
 /**
  * Reads the menu from a JSON object as a Map where the source (file name) is relative to src/main/resource folder
  * @param fileName
  * @param menu
  * @throws JsonParseException
  * @throws JsonMappingException
  * @throws IOException
  * @throws MenuJsfException
  */
 
 @Override
 public void readMenu(String source, MenuModel menuModel)
  throws JsonParseException, JsonMappingException, IOException, MenuJsfException {
  
  Map<String,Object> map=JsonUtils.readMap(true, source );

  MenuFromMap.getMenuFromMap(map, menuModel, null, 0);
  
 }
}

The other one MenuBeanReaderJSONAbsolutePath 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
package org.ximodante.jsf.menu;

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

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

import org.primefaces.model.menu.MenuModel;
import org.ximodante.utils.json.JsonUtils;

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

@Named
@ApplicationScoped
@IMenuBeanReaderType(type="MenuBeanReaderJSONAbsolutePath")
public class MenuBeanReaderJSONAbsolutePath implements IMenuBeanReader {
    
 /**
  * Reads the menu from a JSON object as a Map where the source (file name) is an absolute path
  * @param fileName
  * @param menu
  * @throws JsonParseException
  * @throws JsonMappingException
  * @throws IOException
  * @throws MenuJsfException
  */
 
 @Override
 public void readMenu(String source, MenuModel menuModel)
  throws JsonParseException, JsonMappingException, IOException, MenuJsfException {
  
  Map<String,Object> map=JsonUtils.readMap(false, source );
    
  MenuFromMap.getMenuFromMap(map, menuModel, null, 0);
  
 }
} 

You can think that it is disproportionate the fact of creating a new class that is practically the same and this could be done adding a new boolean parameter to the readMenu method for accepting relative or absolute paths, but this is a learning example.

The application scoped is used as a singleton.


3. Creating the tool class

The purpose of this tool class is managing the parameter (type) of the annotation so that we can extract the desired dependency from all the possible candidates that implements the main interface.

This class is MenuBeanReaderTypeDescription 


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

import javax.enterprise.util.AnnotationLiteral;

/**
 * Class that is a tool to manage the paramemeter "type" from the Annotation IMenuBeanReaderType so that
 * the desired candidate class for injection is selected
 * 
 * @see https://stackoverflow.com/questions/33583032/dynamically-injecting-instances-via-cdi
 * @see https://stackoverflow.com/questions/24798529/how-to-programmatically-inject-a-java-cdi-managed-bean-into-a-local-variable-in
 * 
 * @author Ximo Dante
 *
 */
public class MenuBeanReaderTypeDescriptor extends AnnotationLiteral<IMenuBeanReaderType> implements IMenuBeanReaderType{
 private static final long serialVersionUID = 1L;
 
 private String type;
    
 public MenuBeanReaderTypeDescriptor(String type) {
        this.type = type;
    }
 

 @Override
    public String type() {
         return type;
    }
}


4. Using the injection into a bean

Let's use the MenuBean class from a former post about programmatic menu

Important points:

  1. In the property file, the property menuReaderTypeKey contains the parameter of the annotation IMenuBeanReaderType that chooses the class to be injected (in this case the value is "MenuBeanReaderJSONRelativePath")
  2. The singleton property reader is injected in the bean (the attribute appProps)
  3. The injected Instance<IMenuBeanReader> contains registration of all classes that implements the interface IMenuBeanReader. 
  4. By means of "select" method of the Instance<interface>  object we can retrive the desired injected class passing the parameter (type) of the annotation  




 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
import javax.inject.Named;

import org.primefaces.model.menu.DefaultMenuModel;
import org.primefaces.model.menu.MenuModel;
import org.ximodante.utils.property.ApplicationProperties;

import lombok.Getter;


/**
 * Implements the menu structure as a bean
 * If a bean does not implements 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 menuReaderTypeKey="menuBeanReaderType";
 
 @Getter  
 private MenuModel menuModel;
 
 @Inject 
 ApplicationProperties appProps;
 
 @Inject
 Instance<IMenuBeanReader> unqualifiedMenuBeanReader;
 
 private IMenuBeanReader myMenuBeanReader;
 
 @PostConstruct
        public void init() {
        
       String myReaderType=this.appProps.getProperty(menuReaderTypeKey);
     
       // get desired implementation of menuBeanReader by injection 
       myMenuBeanReader = unqualifiedMenuBeanReader.select(new MenuBeanReaderTypeDescriptor(myReaderType)).get();
     
 }
 
    /**
     * 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 MenuBeanReaderJSONRelativePath().readMenu(fileName, menuModel);
      myMenuBeanReader.readMenu(fileName, menuModel);
      
      
  } catch (IOException | MenuJsfException e) {
   
   e.printStackTrace();
  }
    }
}    
    

The property file application.properties is the same as in the previous post


1
2
3
4
5
webEnvironment=true
menuBeanReaderType=MenuBeanReaderJSONRelativePath
comment=This is a test commennt
kk=Other property
greet.first=Good Morning!

So the injected class is MenuBeanReaderJSONRelativePath, so the the attribute myMenuBeanReader will be transformed to this class instance by the sentence

myMenuBeanReader = unqualifiedMenuBeanReader.select(new MenuBeanReaderTypeDescriptor(myReaderType)).get();

That's all for now. I hope it wil be clearer

jueves, 17 de agosto de 2017

JEE & JSF 5th Part: Accessing property files EASIER

0. Introduction

To show formatted text Hilite is used

The goals of this post are:

  1. Locating the properties file
  2. Creating an ApplicationScoped bean to store these properties
  3. Accessing this bean by injection from another bean.


1. Locating the properties file.


We need to know where to locate the property files. Let's create an easy class to locate the property file. this class is Configuration.java


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


import lombok.Getter;

/**
 * Locate of configuration files "application.properties"
 * @author Ximo Dante
 *
 */
public class Configuration {
 @Getter
 private static final String PropertyFile="config/application.properties";
 
}


This path is relative to src/main/resources folder


2. ApplicationProperty class

This class gets the path to file "application.properties" from last class, loads the properties included in this file and provides a getter method for retrieving the properties. But this class is application scoped, which means that it behaves as a singleton.



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

import java.io.FileInputStream;
import java.io.IOException;
import java.io.Serializable;
import java.util.Properties;

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

import org.ximodante.jsf.config.Configuration;

@Named
@ApplicationScoped
/**
 * Stores properties from file Application.properies whose path can be read in Configuraion.getPropertyfile() 
 * @author Ximo Dante
 *
 */
public class ApplicationProperties implements Serializable{
 private static final long serialVersionUID = 1L;

 private Properties properties;
 
 /**
  * Getter method of the properties by a key
  * @param key
  * @return
  */
 public String getProperty(String key){
  return properties.getProperty(key);
 }
 
 /**
  * The properties are load in the initilization part
  */
 @PostConstruct
    public void init() {
        this.properties = new Properties();
        
        String path = Thread.currentThread().getContextClassLoader().getResource("").getPath() + Configuration.getPropertyFile();
        System.out.println("PROPERTIES.PATH=" + path);
        
        try {
            //this.properties.load(stream);
         this.properties.load(new FileInputStream(path));
        } catch (final IOException e) {
            throw new RuntimeException("Application.properties could not be loaded!");
        }
    }
}


The file application.properties has this content:



1
2
3
4
5
webEnvironment=true
menuBeanReaderType=MenuBeanReaderJSONRelativePath
comment=This is a test commennt
kk=Other property
greet.first=Good Morning!

3. Retrieving properties from a bean


Let's create a bean called TestProperties that injects the last class and retrieves a property, for instance, "menuBeanReaderType" whose value is "MenuBeanReaderJSONRelativePath"



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

import java.io.Serializable;

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

import lombok.Getter;

@Named
@ViewScoped
public class TestProperties implements Serializable{
 private static final long serialVersionUID = 1L;
 
 @Inject 
 ApplicationProperties appProps;
 
 @Getter
 private String myReaderType;
 
 @PostConstruct
    public void init() {
        
     myReaderType=this.appProps.getProperty("menuBeanReaderType");
     System.out.println("Accessed property is:" + myReaderType);
 }
}

And let's modify out later Facelets file tutorial01-menu.xhtml to view the attribute myReaderType as follows


 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
<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.menuModel}"/>
 
        
          </p:layoutUnit>
        
          <p:layoutUnit position="center">
            Content panel PROPERTY GOT:--> #{testProperties.myReaderType}    
            <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>Serializable{
 private static final long serialVersionUID = 1L;
 
 @Inject 
 ApplicationProperties appProps;
 
 @Getter
 private String myReaderType;
 
 @PostConstruct
    public void init() {
        
     myReaderType=this.appProps.getProperty("menuBeanReaderType");
     System.out.println("Accessed property is:" + myReaderType);
 }
}


4. Running ...

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

and the result is:





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 !


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