martes, 9 de febrero de 2021

38. How to generate the WAR file? Solving some problems (org.codehaus.stax2.ri.SingletonIterator)

1. Procedure

 1. Change the file "dao.properties" and change the position of the comments, so that it points to the remote server:

#-------------------------------------------------------------------------------------------------------------------------------------
#PERSISTENCE UNITS AND HOSTS: 0:Control, 1:Postgres Gexflow1, 2:H2 Sicres ORVE, 3:Postgres Gexflow2, 4:SQL-SERVER Gestio-3 Casdastre, 5:SQL-SERVER Aytos, 6:pg Sicres ORVE
#-------------------------------------------------------------------------------------------------------------------------------------
#                 0            1                     3        4                      5                    6        7
persistence.units=control_post;PostgressGexflowJPAPU;sicresh2;sqlserver-gestio3-jtds;sqlserver-aytos-jtds;sicrespg;control_local
#persistence.units=control_local;PostgressGexflowJPAPU;sicresh2;sqlserver-gestio3-jtds;sqlserver-aytos-jtds;sicrespg;control_local

#                 0             1             2            3             4             5             6 
#                 pg-Control    pg-Gexflow    h2-ORVE      mssql-Cadatre mssql-Aytos   pg-ORVE       pg-control_local
persistence.hosts=192.168.28.XX;192.168.28.XX;192.168.10.XX;192.168.28.XX;192.168.28.XX;192.168.28.XX;192.168.10.XX
#persistence.hosts=192.168.10.XX;192.168.28.XX;192.168.10.XX;192.168.28.XX;192.168.28.XX;192.168.28.XX;192.168.10.XX

#                         0    1    2     3    4    5    6
persistence.units.active=true;true;false;true;true;true;false


2. Uncomment these 2 dependencies from the pom.xml

<dependency> 
 <groupId>org.codehaus.woodstox</groupId>
  <artifactId>stax2-api</artifactId>
  <version>4.2.1</version>
  <scope>runtime</scope>
</dependency>
		 
<dependency>
 <groupId>org.codehaus.woodstox</groupId>
  <artifactId>stax2-api</artifactId>
  <version>3.1.4</version>
  <scope>runtime</scope>
</dependency>

Take into account that these 2 dependencies are needed if:

  1. A class a FirtControlLoad is executed from eclipse as a Java application
  2. When deployed to the WebProp server
But it generates errors when not commented (due to Hibernate Envers) when executed from Eclipse as:  Run as ->Run on Server.

3. Verify that the file context.xml in "webapp/METAINF" folder points to the remote DB server IP

<?xml version="1.0" encoding="UTF-8"?>
<Context path="/openweb" docBase="openweb"
        debug="5" reloadable="true" crossContext="true">
        
    <!-- url="jdbc:postgresql://localhost:5432/openweb"/> -->  
    <!-- OJO: Estamos en BDProp 192.168.28.29 -->
    <Resource 
      name="control_post"
      auth="Container"
      type="javax.sql.DataSource"
      username="USER"
      password="PASWORD"
      maxTotal="50"
      maxIdle="20"
      maxWait="10000"
      timeBetweenEvictionRunsMillis="180000"
      removeAbandonedOnMaintenance="true"
      removeAbandonedOnBorrow="true"
      driverClassName="org.postgresql.Driver"
      url="jdbc:postgresql://192.168.28.XX:5432/openweb" 
    />
      
    <!-- Added by Edu  @see https://stackoverflow.com/a/19003725/7704658 -->
    <!--  
    <Resource name="BeanManager" 
        auth="Container"
        type="javax.enterprise.inject.spi.BeanManager"
        factory="org.jboss.weld.resources.ManagerObjectFactory"/>  
    -->    
</Context>


4. Shutdown the tomcat server (tomcat_folder/bin/shutdown.sh) 

5. Generate the war : From Eclipse: Run as -> Maven build:

  • Goals: clean package
  • Profiles: production

6.Copy the file "xxx.war" from the target folder to the tomcat_folder/webapps and rename it to "WEBPROP.war"

7. Start up the tomcat server  (tomcat_folder/bin/startup.sh) 


8. Optional: If the application cannot run OK, then you should drop the "control" schema from the DBProp Postgress server and re-create it again.

2. Problem solving

1.  If the application cannot run OK, then you should drop the "control" schema from the DBProp Postgress server and re-create it again.

2. These error appears: 
Caused by: java.lang.NoSuchMethodError: 'org.codehaus.stax2.ri.SingletonIterator org.codehaus.stax2.ri.SingletonIterator.create(java.lang.Object)'
This error is caused because:
a. You have not uncommented the 2 dependencies from org.codehaus.woodstox in step2 and you are using the "WAR file" in the remote tomcat server, or 
b. You have not commented on these 2 dependencies in the pom.xml and you are executing the program from eclipse in the local machine  

miércoles, 20 de enero de 2021

37. My Wonderful Web Framework. (2) Actions. Defining the messagingsocket server

1. Introduction

There are 2 servers:

  1. Web server that makes the request to the socket server
  2. The Socket server that receives the request and executes a shell program
The shell program may include a shell script or it can execute a java program. The call to this programs is similar:


2. The SocketAction class in the webserver


This class inherits from BaseExecute that defines the base facilities.

It has a method "execute" that receives the fix and form params from the "application.yaml" file that is in the IFXX java project, for instance:

fixParams: '[STRING:JAVA_PATH -jar JARS_FOLDER/IF/MiJar.jar AYTOSOPERATION]', formParams: '{name: s_n, type: STRING, length: 1, value: S, required: true }'

In addition, it constructs a message to send to the socket server adding these additional parameters to the fix and form parameters:
  1. INET address of the user
  2. user name
  3. user-agent (Mozilla, Chrome..)
  4. Session-Id (HTTP)
Then the socket server IO and port is got from the properties: "var server".ip and "var server".port from the "app.properties" file.

The "var server" is an attribute of the action. By default, the value is "socket". This value is set in the application.yaml file when defining an action

server: socket,

The logic of sending and receiving messages with the server is defined in the procedure sendMessageCommand :

 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
	private void sendMessageCommand(String msg) throws IOException {
    	//System.out.println("Sending to server:"+ msg);
		String fileName1=null;
		byte[] bytes=null;
		String fileName1Key="(DOWNLOADER-FILENAME)";
		String bytesKey="(DOWNLOADER-BYTES)";
		String nRecords="(N.RECORDS=)";
    	out.println(msg);
    	
        String line = in.readLine().trim();
        
        //ERROR:* means that an error has occurred 
        //FIN:* means the end of the process
        while (! line.toUpperCase().startsWith("ERROR") &&
        	   ! line.toUpperCase().startsWith("FIN:")) { 
        	
        	if (line.contains(fileName1Key)) {
        		int pos=line.indexOf(fileName1Key);
        		fileName1=line.substring(pos+fileName1Key.length());
        	} else if (line.contains(bytesKey)) {
        		int pos=line.indexOf(bytesKey);
        		bytes=Base64.getDecoder().decode(line.substring(pos+bytesKey.length()));
        		bytes=ZipUtils.decompress(bytes);
        		addDownloaderEdu(fileName1, bytes);
        	} else if (line.contains(nRecords)) {
        		aS[2]=line.substring(line.indexOf(nRecords)+nRecords.length());
        		showMessage(line);
        	}
        	else showMessage(line, line.contains("ERROR"));
        	line = in.readLine();
        	//getAfectedRecords(line);
        }
        if (line.toUpperCase().startsWith("ERROR")) showError(line);
        else showMessage(line, line.contains("ERROR"));
    }

We can see that the condition for finishing the communication is that the message line starts with "ERROR" or "FIN:" . The first key indicates that an error has occurred, and the second one that the end of the process has been reached.

there are other keys that can contain the line message:
  1. (DOWNLOADER-FILENAME) and (DOWNLOADER-BYTES): To create a component called "DownloaderEdu" that downloads a file whose name is after the first key and the byte array to download is after the second key. NOTE that the byte arrays are compressed with ZIP and transformed to Base64. 
  2. (N.RECORDS=) the number of affected records by the process is sent after this key

3. The Socket server

There are 3 important files:
  • app.properties:  defines port to listen and paths to java and shells scripts
  • SimpleSocketServer class: the class with the main method to be executed once and manage the requests
  • ServerRequestHandler: Manages the treatment of the requests.
Here is the code:

app.properties:

1
2
3
4
5
#---location of shells scripts and java programs to be executed as external options
socket.port = 6666
java.path = /home/USER/MyPrograms/jdk-15.0.1/bin/java
jars.folder = /home/OTHERUSER/MyJars
shells.folder = /home/OTHERUSER/MyShells

SimpleSocketServer.java:


 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
package u.requests.sockets;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Properties;

import utils.PropertiesUtils;

/**
 * Only open port for serving messages
 * The important part is the access to ServerRequestHandler03 that manages all the logic
 * @author ximo
 *
 */
public class SimpleSocketServer extends Thread
{
    private boolean isExecutedFromJar=false;  //????? Cambiar al compilar
	private ServerSocket serverSocket;
    private int port;
    private boolean running = false;
    //private String propsFileName="properties"+File.separator+"app.properties";
    private Properties props=null;

    public SimpleSocketServer(){
    	port=6666;
    	try {
			props=PropertiesUtils.getProperties(isExecutedFromJar,"app");
			port=Integer.parseInt(props.getProperty("socket.port",""+port));
		} catch (Exception e) {e.printStackTrace();}
    }
    
    public void startServer() {
        try {
            serverSocket = new ServerSocket( port );
            this.start();
        } catch (IOException e) { e.printStackTrace(); }
    }

    public void stopServer() {
        running = false;
        this.interrupt();
    }

    @Override
    public void run() {
        running = true;
        //This is for accepting multiple connections
        while( running ) {
            try {
                System.out.println( "System.out.println-->Listening for a connection" );

                // Call accept() to receive the next connection
                Socket socket = serverSocket.accept();

                // Pass the socket to the RequestHandler thread for processing
                ServerRequestHandler requestHandler = new ServerRequestHandler(socket, props );
                requestHandler.start();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main( String[] args ) {
    	SimpleSocketServer server=null;
    	try {
    		server=new SimpleSocketServer();
    		System.out.println( "SimpleSocketServer03.Start server on port: " + server.port );
			server.startServer();
			/*
        	// Automatically shutdown in 1 minute
        	try {
            	Thread.sleep( 60000 );
        	} catch( Exception e ) { e.printStackTrace(); }
			 */
		} catch (Exception e) {	
			if (server!=null) {
				server.stopServer();
				System.out.println("server stopped");
				e.printStackTrace();
			}	
		}	
    }
}


ServerRequestHandler.class:

 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
package u.requests.sockets;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Properties;

import utils.CmdUtils;

class ServerRequestHandler extends Thread {

    private Socket socket;
    private String command="";
    //private String myPath="";
    private String shellsFolder="";
    private String jarsFolder="";
    private String javaPath="";
    private Properties props=null;
    private Boolean isError=false;
    private BufferedReader inSocket =null;
    private PrintWriter outSocket=null;
    private ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    
    public ServerRequestHandler(Socket socket, Properties props ) { 
    	this.socket = socket; 
    	this.props=props;
    }
    
    @Override
    public void run() {
    	
        try {
            System.out.println( "RH-->Received a connection" );

            // Get input and output streams
            inSocket = new BufferedReader( new InputStreamReader( socket.getInputStream() ) );
            outSocket = new PrintWriter( socket.getOutputStream(), true );
            
            
            command = inSocket.readLine();
            outSocket.println("4.1 Des del server: Obtenint comandament a executar...("+ command +")");
            
            if (command.trim().length() <2) showError("ERROR: Comandament curt:("+ command + ")");
            else {
            	try {
            	
				shellsFolder=props.getProperty("shells.folder").trim();
				jarsFolder=props.getProperty("jars.folder").trim();
				javaPath=props.getProperty("java.path").trim();
				//command=myPath.trim() + File.separator+ command;
				command=command
						.replace("JAVA_PATH", javaPath)
						.replace("SHELLS_FOLDER", shellsFolder)
						.replace("JARS_FOLDER", jarsFolder);
				System.out.println("COMMAND="+ command);
				} catch (Exception e) { showError("ERROR : Obrint paràmetres del fitxer de propietats... app.properties" + e.getMessage());}
		    }
            if (!isError) {
            	outSocket.println("4.2 Des del server: Executant...("+ command +")");
            	try {
    				//Execute and wait for end of execution
    				if(CmdUtils.execProgram(command, null, null, false, outputStream) !=0) showError("ERROR 4: Executant " + command + "... ");
    			}catch (Exception e) { showError("ERROR 4.2: Executant " + command + "... " + e.getMessage());}
    		}
            
            if (!isError) {
            	outSocket.println("4.3 Des del server: Informant ...");
            	try {
    				//Show execution details
            		int i=0;
            		for (String line : outputStream.toString().split("\\n")) {
            			String lineUP=line.toUpperCase();
            			if (lineUP.contains("SHOWMESS") || lineUP.contains("ERROR")) {
            				outSocket.println("4.3." + ++i + "-->" + line);
            				System.out.println("4.3." + i + "-->" + line);
            			}	
            		}	
    			}catch (Exception e) { showError("ERROR 4.3: Executant " + command + "... " + e.getMessage());}
    		}
            
            if (!isError) outSocket.println("FIN:");  //END OF PROCESS SIGNAL!!
                      
            // Close our connection
            inSocket.close();
            outSocket.close();
            socket.close();

            System.out.println( "Rh:->Connection closed" );
        } catch( Exception e ) { e.printStackTrace(); }
    }
        
    private void showError(String message) {
    	isError=true;
        outSocket.println(message);
    }
}



36. My Wonderful Web Framework. (1) Actions. YAML definition

 1.  Actions

Falta definir els métodes de la classe actual. Cal fer un wrapper per a que una classe  que assumisca una cride d'una altra i que herede de BaseExecute!!!!!


An action consists of executing a method from a class. The actions can be defined in menus and forms:

  1. Actions that are executed in a "MenuItem" of type "action" ("command" and "1" are synonymous). Only one action per "MenuItem" is allowed
  2. Actions that are executed inside a form (represented by a class) in a "MenuItem" of type "form" (take into account that there are several types of forms: default(2), custom(3) and yaml (4)
An action is defined by:
  1. The class that contains the method to execute
  2. The method to execute
  3. The parameters that accept the class. That can be fixed and/or asked by means of a form
  4. The type of action: the possible values are: inner: In this case the class to execute is in the same Vaadin module, shell: In this case a call to a shell script in the property "execution.folder" of the property file "app.properties", socket: This is very important as delegates the execution to another server by means of sending messages through sockets, avoiding java RMI
  5.  Optional information: role group that is allowed to execute the program, icon to show, ask for confirmation, char to show in combo in grid form, if need a record id to operate,

2. Defining the actions

Here is a snippet of YAML code defining both types of actions $$$


 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
menuItems: 
  [
    #1. MenuItem with an action of type socket (executed in another server (action: socket type))
    { description: firstcontrolloadyaml_control, className: openadmin.actions.SocketActions, icon: fa-upload,   
     type: action, defaultActions: false,
     actions: 
       [ 
         { name: dataLoad,  type: socket, method: execute,    
           fixParams: '[STRING:JAVA_PATH -jar JARS_FOLDER/IF/MiJar.jar FIRSTCONTROLLOAD]', 
           roleGroup: gadmin, icon: fa-upload, executedInGrid: false, needId: false  
         } 
       ]
    },  

    #2. MenuItem with an action of type inner (executed in a class included in the "war" file (action: inner type))
    { className: openadmin.actions.u.request.MetadataLoader, icon: fa-upload,   
      type: action, defaultActions: false, 
      actions: 
        [ 
          { name: dataLoad,  method: dataLoad,  fixParams: '[DAO-METADATA]',   
            roleGroup: gadmin,   icon: fa-upload, executedInGrid: false, needId: false  
          } 
        ]
    },
   
    #3. A form of type custom that shows the "users" and has som actions 
    { className: openadmin.model.control.User, description: User_Custom, icon: fa-user, combo: false, type: custom,
      actions: 
        [ 
          #3.1 Action in the form that calls a java program in another server (action: socket type)
          { name: getFactures, confirm: false, idChar: E, type: socket, method: execute, 
            fixParams: '[STRING:JAVA_PATH -jar JARS_FOLDER/IF/MiJar.jar AYTOSOPERATION]', 
            formParams: '{name: s_n, type: STRING, length: 1, value: S, required: true }', 
            roleGroup: gnew,   icon: fa-file-invoice-dollar, executedInGrid: false, needId: false  
          } ,
          
          #3.2 Example where the class of the action is a shell script executed in the same server (action: shell type)
          { name: dni,  confirm: false, idChar: 7, method: verifyDNI, roleGroup: gadmin,  type: shell, icon: fa-id-card, 
            fixParams: '[STRING:bshComprobaciones.sh];[ID];[STRING:verifyDNI]'    
          } ,

          #3.3 Example where the class of the action is the same class (action: inner type)
          { name: kkerr, confirm: false,   idChar: 8, type: inner,  method: kk1,            
            fixParams: '[ID]', roleGroup: gadmin,   icon: fa-cloud-showers-heavy    
          },  
          
          #3.4 Action in the form that calls a shell script in another server (action: socket type)
          { name: prova, confirm: true,    idChar: C, type: socket, executedInGrid: false,  
           fixParams: '[STRING:SHELL_PATH/shProva01.sh]', roleGroup: gadmin,   icon: fa-beer    
          } 
        
        ]
    }
  ] 


Line 4 defines a "menuitem" that is of type action, so it has only one action of type socket

Line 14 defines a "menuitem" that is of type action, so it has only one action of type inner


Line 26 defines a form with 4 actions of different types Socket (external java program execution), shell (in the same server), inner with a method of the "entity class" and another Socket (external shell script in another server)

Here are some important parameters:
  • className: The class that owns the method to execute, by default is the class shown in the form
  • method: the name of method of the class to execute. By default is the "execute"
  • name: The name that will be shown to the user and will be converted to i18n (internationalization)
  • roleGroup: the authorized group top execute the method
  • idChar: The char to show in the group of actions (in the combo) to be executed in a form. It must be set when the actions are executed from a form. Each action should have a unique "idChar"
  • icon: The name of the "font-awesome icon" to show.
  • needId: If the action is executed on one record (that is identified by its Id). The Id is supplied by the current record.
  • executedInGrid: If this action can be executed in a record of the grid and showed in the combo of options
  • confirm: If ask for confirmation of the user before executing the action. 

3. The fixParams parameter

This parameter is important as it has a peculiar format. It is a list of constant parameters. Every parameter must be between "square brackets" "[ ]" and separated by a colon ";"

Here is an example:


fixParams: '[DAO-H2];[ID];[INT:2];[LONG:9];[STRING:cat];[SERVER:0]', 

Where:
  • DAO-H2 is the DAO Object form the H2 database, there ara also other DAOs to other persistence units.
  • ID is the id of the Base Object
  • INT:2 is the integer value "2"
  • LONG:9 is the long value "9"
  • STRING:cat is the String value "cat"
  • SERVER:0 returns the server + ":" + port for specifiying a socket to a server to pass messages.
Al all these parameters are stored in a List

4. The formParams parameter

This parameter is important as it has a peculiar format. It is a list of parameters that will be required within a form to the user.

Each parameter is defined between brackets "{ }" and forms a YAML structure.


formParams: '{name: year, type: INT, length: 4, value: $YEAR, required: true }, {name: registro, type: INT, length: 6, value: 0, required: true }',

The labels of the parameters are:
  • name: name of the parameter
  • type: INT, LONG, STRING ...
  • length: max number of chars of the parameter 
  • required; true of false
  • value: default value. It can be:
    • $YEAR : Actual year
    • $YEAR_PREVIUOS_MONTH: Year of the previous month
    • $MONTH: Actual month (1 to 12) 
    • $PREVIOUS_MONTH: The previous month (1 to 12)
    • A constant

lunes, 12 de octubre de 2020

35. Migrating to Vaadin version 17 & 18. Problems in annotaitons: @Push and @Theme

see: Vaadin Forum

1.VaadinServlet/CdiVaadinServlet implementions

In version 14

 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 openadmin.listeners;

import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;

import com.vaadin.cdi.CdiVaadinServlet;
import com.vaadin.flow.server.Constants;
//import com.vaadin.flow.server.VaadinServlet;


/**
 * Information about app servlet 
 * 
 * Necessary for defining i18n Provider
 * @author ximo 
 *
 */
@SuppressWarnings("serial")
@WebServlet(
	urlPatterns = "/*", 
	name = "slot", 
	asyncSupported = true, //For @Push
	
	
	initParams = {
		// I18N Provider for translation of labels	
        //Vaadin 14,15,16
	@WebInitParam(name = Constants.I18N_PROVIDER, value = "openadmin.i18n.MyI18nProvider"),	
	// Enable pmpm instead of npm	2020-05-27 for Vaadin 15 
	// @see https://dantesquevaadin.blogspot.com/2020/05/32-vaadin-15-new-version-using-pnpm.html
	@WebInitParam(name = "pnpm.enable", value = "true")
	})

// Deprecated. @see https://vaadin.com/docs/v14/flow/production/tutorial-production-mode-basic.html
//@VaadinServletConfiguration(productionMode = false)

//public class ApplicationServlet extends VaadinServlet {
public class ApplicationServlet extends CdiVaadinServlet {
}


now in version 17, there are several changes


 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
package openadmin.listeners;

import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;

import com.vaadin.cdi.CdiVaadinServlet;
//import com.vaadin.flow.server.VaadinServlet;
import com.vaadin.flow.server.InitParameters;

/**
 * Information about app servlet 
 * 
 * Necessary for defining i18n Provider
 * @author ximo 
 *
 */
@SuppressWarnings("serial")
@WebServlet(
	urlPatterns = "/*", 
	name = "slot", 
	asyncSupported = true, //For @Push
	
	//pushmode = "manual",
	
	initParams = {
		//Vaadin 17
		// I18N Provider for translation of labels	
     		@WebInitParam(name = InitParameters.I18N_PROVIDER, value = "openadmin.i18n.MyI18nProvider"),
        	@WebInitParam(name = InitParameters.SERVLET_PARAMETER_PUSH_MODE, value= "PushMode.MANUAL" ),
        	@WebInitParam(name = InitParameters.SERVLET_PARAMETER_ENABLE_PNPM, value= "true" ),
        	//End Vaadin17
	})

//public class ApplicationServlet extends VaadinServlet {
public class ApplicationServlet extends CdiVaadinServlet {
}


2.VaadinServiceInitListener implementions and @Push

In version 14, the @Push annotation was placed  in the main form, now in version 17 gives a runtime error: 

Found app shell configuration annotations in non `AppShellConfigurator` classes.

Please create a custom class implementing `AppShellConfigurator` and move the following annotations to it:

    - @Push from openadmin.ui.forms.MainView

So, the class implementing VaadinServiceInitListener should now implement also the AppShellConfigurator interface and will contain the @Push annotation

Now let's see the new class with the old version aspects commented


In version 18 also complains about the @Theme annotation and does not want it in the main form



 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
package openadmin.listeners;


import com.vaadin.flow.component.page.AppShellConfigurator;
import com.vaadin.flow.component.page.Push;
import com.vaadin.flow.server.ServiceInitEvent;
import com.vaadin.flow.server.VaadinServiceInitListener;
import com.vaadin.flow.shared.communication.PushMode;

import openadmin.ui.forms.LoginForm;
import openadmin.vaadin.http.SessionParams;
import openadmin.vaadin.http.VaadinHttpUtils;



/**
 * This class is used to listen to BeforeEnter event of all UIs in order to
 * check whether a user is signed in or not before allowing entering any page.
 * It is registered in a file named
 * com.vaadin.flow.server.VaadinServiceInitListener in META-INF/services.
 * 
 * NOTE that a SessionScopped object cannot be injected so, to know if the user is logged in
 *   we need to make use of information stored in the Session object with keys "CURRENT_USER" 
 */




@SuppressWarnings("serial")
//Vaadin14,15,16
//public class LoggedInListener implements VaadinServiceInitListener {

//Only Vaadin 17
@Push(PushMode.MANUAL)

//Only for Vaadin 18 +
@Theme(value = Lumo.class)

public class LoggedInListener implements VaadinServiceInitListener, AppShellConfigurator {
	. . . . . . . . 

remember to remove the@Push and @Theme annotations from the main form!!!!

jueves, 1 de octubre de 2020

34. Understanding ui.access(). @Push annotation

 0. Introduction

I want to show only a few remarks about "Asynchronous Updates" from Vaadin tutorial.

When you run a thread,  the Session seems to be not accessible, so the UI. You should take it into account as when manipulating form components from the thread!!!


1. Hanging the execution

Be very careful as the execution may hang in these cases:

  • When using the annotation @Push without options and the process is executed for the first time. Maybe I am doing something wrong but I don't know.
  • When using the annotation @Push( (PushMode.MANUAL) and not using ui.push() as the final sentence inside a ui.access()(() -> {...}) block; to assure the UI is updated
  • When using ui.push(), my advice is to put it into a try-catch block!
  • In Vaadin-14 use the @Push annotation in the main form, but in Vaadin-17 use it in the class that implements AppshellConfigurator. As it is explained in the next post.

ui.access(() -> {
  Span sp=new Span("Begin command...");
  view.add(sp);
  try {ui.push()} catch (Exception e){e.printstacktrace()}; //Last sentence 
});


2. Creating components dynamically from a thread

Components should be created within the ui.access() {} block, as you cannot access the Session or the UI outside this block. 

For instance, you cannot create an Anchor component outside this block as the creation implies access to the UI for getting URIs!!

In case you can create the component, you cannot attach it to the UI outside this block!

miércoles, 9 de septiembre de 2020

33. Detecting and solving problems Vaadin 16.0.3 and 17.0.1

 1. Incompatible pnpm versions

Vaadin wants pnpm versions in a determinate range. So DO NOT install pnpm. Vaadin will install it for you.


2.  pnpm fails when downloading resources

2.1 See previous error (1)

2.2 Delete these files from the main folder of the project:

- package.json

- pnpmfile.js

- pnpm-lock.yaml

- {}tsconfig.json

- types.d.ts

- webpack.config.js

- webpack.generated.js

- mode_modules (folder)


2.3 reset the project as follows: 

2.3.1 Select the parent project and Run As - Maven Clean

2.3.2 Select the parent project and Maven - Update Project

2.3.3 Select the parent project and Run As - Maven Build .. - and goals: "clean package" and profiles: "production"

2.3.4. Select the vaadin (child) project and Run As - Maven Clean

2.3.5 Select the  vaadin (child) project and Maven - Update Project (select all the project that is a dependant)


3.  Errors in some json files in the node_modules folder

These errors are causes by comments and duplicate assignments of parameters.

Modify these files and remove commends and duplicated assignments


4.  Java 14.!!!!

There can be some problems in Eclipse using Java 14+. see My Spanish blog Informatica Dantesca.

5.  @Push annotation and Vaadin 17.0.x

The @Push annotation does not work still in Vaadin 17.0.1 unless you use it in the appropriate class. It is a problem with new versions. It should be used in another class. See the previous post

sábado, 16 de mayo de 2020

32. Vaadin 15. New version. Using pnpm instead of npm

1. Introduction
Vaadin proposes to user pnpm instead of npm as it seems quicker and saves disk space.

To achieve this goal one can use several ways, see Vaadin 15 for complete details:
  1. In Spring: in the application.properties file, set this property vaadin.pnpm.enable=true
  2. In a plain JEE application with the @WebServlet annotation (is the one I use)
  3. In a plain JEE application by means of the web.xml file
  4. In the pom.xml file, in the part of the vaadin-maven-plugin

My option is using the @WebServlet annotation, as in my project, I used this class to define the management of internationalization and CDI. Here is the source code


 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
package openadmin.listeners;

import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;

import com.vaadin.cdi.CdiVaadinServlet;
import com.vaadin.flow.server.Constants;

/**
 * Information about app servlet 
 * 
 * Necessary for defining i18n Provider
 * @author ximo 
 *
 */
@SuppressWarnings("serial")
@WebServlet(
	urlPatterns = "/*", 
	name = "slot", 
	asyncSupported = true, 
	initParams = {
		
	// I18N Provider for translation of labels	
        @WebInitParam(name = Constants.I18N_PROVIDER, value = "openadmin.i18n.MyI18nProvider"),
        
        // Enable pmpm instead of npm	
        @WebInitParam(name = "pnpm.enable", value = "true")
 	})


public class ApplicationServlet extends CdiVaadinServlet {
}