Quantcast
Channel: Magnus K Karlsson
Viewing all 526 articles
Browse latest View live

JPA 2.2 MySQL 8 Mapping Boolean, Date, Enum and CLOB

$
0
0

Here I will show how to map Boolean, java.util.Date, Boolean and enum in JPA 2.2 (Java EE 8) and test it against MySQL 8 CE and in-memory database H2.

>package se.magnuskkarlsson.clearca.ca.entity;

import java.util.Date;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Lob;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;

@Entity
public class User {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(columnDefinition = "BIT")
private Boolean enabled;

@Temporal(TemporalType.TIMESTAMP)
@Column
private Date created;

@Enumerated(EnumType.STRING)
@Column
private Status status;

// https://dev.mysql.com/doc/refman/8.0/en/storage-requirements.html#data-types-storage-reqs-strings
@Lob
@Column(columnDefinition = "TEXT") // 2^16 = 65 536 = 65 KB
private String data1;

@Lob
@Column(columnDefinition = "MEDIUMTEXT") // 2^24 = 16 777 216 = 16 MB
private String data2;

@Lob
@Column(columnDefinition = "LONGTEXT") // 2^32 = 4 294 967 296 = 4 GB
private String data3;

// ----------------------- Logic Methods -----------------------

// ----------------------- Helper Methods -----------------------

public enum Status {

ACTIVE, DISABLE;

}

// ----------------------- Get and Set Methods -----------------------

public Long getId() {
return id;
}

public boolean isEnabled() {
return enabled;
}

public User setEnabled(boolean enabled) {
this.enabled = enabled;
return this;
}

public Date getCreated() {
return created;
}

public User setCreated(Date created) {
this.created = created;
return this;
}

public Status getStatus() {
return status;
}

public User setStatus(Status status) {
this.status = status;
return this;
}

public String getData1() {
return data1;
}

public User setData1(String data1) {
this.data1 = data1;
return this;
}

public String getData2() {
return data2;
}

public User setData2(String data2) {
this.data2 = data2;
return this;
}

public String getData3() {
return data3;
}

public User setData3(String data3) {
this.data3 = data3;
return this;
}

}

And src/test/resources/META-INF/persistence.xml with 2 persistence unit for integration testing.

><?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_2.xsd"
version="2.2">

<persistence-unit name="IT" transaction-type="RESOURCE_LOCAL">
<class>se.magnuskkarlsson.clearca.ca.entity.User</class>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect" />
<property name="hibernate.connection.driver_class" value="org.h2.Driver" />
<property name="hibernate.connection.url" value="jdbc:h2:mem:" />
<property name="hibernate.hbm2ddl.auto" value="create-drop" />

<property name="hibernate.show_sql" value="true" />
<property name="hibernate.format_sql" value="true" />
<property name="hibernate.generate_statistics" value="false" />
<property name="hibernate.cache.infinispan.statistics" value="false" />
</properties>
</persistence-unit>

<persistence-unit name="IT-MySQL" transaction-type="RESOURCE_LOCAL">
<class>se.magnuskkarlsson.clearca.ca.entity.User</class>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL57InnoDBDialect" />

<!-- <property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver" /> -->
<property name="hibernate.connection.driver_class" value="com.mysql.cj.jdbc.Driver" />
<property name="hibernate.connection.url"
value="jdbc:mysql://localhost:3306/clearca?serverTimezone=UTC" />
<property name="hibernate.connection.username" value="root" />
<property name="hibernate.connection.password" value="root" />
<property name="hibernate.hbm2ddl.auto" value="create-drop" />

<property name="hibernate.show_sql" value="true" />
<property name="hibernate.format_sql" value="true" />
<property name="hibernate.generate_statistics" value="false" />
<property name="hibernate.cache.infinispan.statistics" value="false" />
</properties>
</persistence-unit>

</persistence>

And maven dependency for JUnit testing

><hibernate.version>5.3.14.Final</hibernate.version>
<hibernate-validator.version>6.0.18.Final</hibernate-validator.version>
...
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.199</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.17</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-envers</artifactId>
<version>${hibernate.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>${hibernate-validator.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>jakarta.el</artifactId>
<version>3.0.2</version>
<scope>test</scope>
</dependency>

And junit code for integration Test

>package se.magnuskkarlsson.clearca.ca.entity;

import java.util.Date;
import java.util.Locale;

import javax.persistence.EntityManager;
import javax.persistence.Persistence;

import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

import com.github.javafaker.Faker;

import se.magnuskkarlsson.clearca.Configuration;

public class UserMySQLIT {

private static EntityManager em;

private final Faker faker = new Faker(new Locale("sv-SE")); // default Locale("en", "")

@BeforeClass
public static void oneTimeSetUp() throws Exception {
new Configuration().getCryptoProvider();
em = Persistence.createEntityManagerFactory("IT-MySQL").createEntityManager();
}

@Before
public void setUp() throws Exception {
em.getTransaction().begin();
}

@After
public void tearDown() {
em.getTransaction().commit();
}

@AfterClass
public static void oneTimeTearDown() throws Exception {
}

// ----------------------- Test Methods -----------------------

@Test
public void getCert() throws Exception {
User user = new User() //
.setEnabled(true) //
.setCreated(new Date()) //
.setData1(faker.lorem().fixedString(65 * 1000)) //
.setData2(faker.lorem().fixedString(16 * 1000 * 1000)) //
.setData3(faker.lorem().fixedString(4 * 1000 * 1000 * 1000));

em.persist(user);

System.out.println("Persisted: " + user.getId());
}

}

And when run.

>...
Nov 17, 2020 8:38:10 AM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH10001005: using driver [com.mysql.cj.jdbc.Driver] at URL [jdbc:mysql://localhost:3306/clearca?serverTimezone=UTC]
...
Nov 17, 2020 8:23:42 AM org.hibernate.dialect.Dialect <init>
INFO: HHH000400: Using dialect: org.hibernate.dialect.MySQL57InnoDBDialect
...
create table User (
id bigint not null auto_increment,
created datetime(6),
data1 TEXT,
data2 MEDIUMTEXT,
data3 LONGTEXT,
enabled BIT,
status varchar(255),
primary key (id)
) engine=InnoDB

==================================

Nov 17, 2020 8:20:46 AM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH10001005: using driver [org.h2.Driver] at URL [jdbc:h2:mem:]
...
Nov 17, 2020 8:20:46 AM org.hibernate.dialect.Dialect <init>
INFO: HHH000400: Using dialect: org.hibernate.dialect.H2Dialect
...
create table User (
id bigint generated by default as identity,
created timestamp,
data1 TEXT,
data2 MEDIUMTEXT,
data3 LONGTEXT,
enabled BIT,
status varchar(255),
primary key (id)
)

How to Log Hibernate SQL Query , Parameter Values and Execution Time with P6Spy and JBoss EAP/Wild

$
0
0

You can configure Hibernate to log SQL query via

><property name="hibernate.show_sql" value="false" />

But that is not the best solution, because Hibernate:

  • Logs to Standard Output.
  • Does not log SQL Parameters (since Hibernate uses PreparedStatement, and the values are not available when created the PreparedStatement)
  • Does not out-of-the box logs the Execution TIme.

A better solution is to use P6Spy.

First we gona use P6Spy in automatic Integration Test and in-memory database H2 Cheet Sheat. So first add Maven test dependency.

><!-- Hibernate 5 and EAP 7.3 Version -->
<hibernate.version>5.3.14.Final</hibernate.version>
<hibernate-validator.version>6.0.18.Final</hibernate-validator.version>

<!-- H2 in-memory database -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.199</version>
<scope>test</scope>
</dependency>
<!-- MySQL 8 Community Edition, CE -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.17</version>
<scope>test</scope>
</dependency>
<!-- Hibernate 5 -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-envers</artifactId>
<version>${hibernate.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>${hibernate-validator.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>jakarta.el</artifactId>
<version>3.0.2</version>
<scope>test</scope>
</dependency>
<!-- P6Spy -->
<dependency>
<groupId>p6spy</groupId>
<artifactId>p6spy</artifactId>
<version>3.9.1</version>
<scope>test</scope>
</dependency>

And now modify our src/test/resources/META-INF/persistence.xml. Note the updated hibernate.connection.driver_class and hibernate.connection.url.

><?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_2.xsd"
version="2.2">

<persistence-unit name="IT" transaction-type="RESOURCE_LOCAL">
<class>se.magnuskkarlsson.example.javaee8_p6spy.entity.User</class>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect" />
<!-- <property name="hibernate.connection.driver_class" value="org.h2.Driver" /> -->
<!-- <property name="hibernate.connection.url" value="jdbc:h2:mem:" /> -->
<property name="hibernate.connection.driver_class" value="com.p6spy.engine.spy.P6SpyDriver" />
<property name="hibernate.connection.url" value="jdbc:p6spy:h2:mem:" />

<property name="hibernate.hbm2ddl.auto" value="create-drop" />

<property name="hibernate.show_sql" value="false" />
<property name="hibernate.format_sql" value="false" />
<property name="hibernate.generate_statistics" value="false" />
<property name="hibernate.cache.infinispan.statistics" value="false" />
</properties>
</persistence-unit>

</persistence>

And in our IT class everything is the same. For rendering test data I use Generating Test Data - JavaFaker.

>package se.magnuskkarlsson.example.javaee8_p6spy.control;

import java.util.Locale;

import javax.persistence.EntityManager;
import javax.persistence.Persistence;

import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

import com.github.javafaker.Faker;

import se.magnuskkarlsson.example.javaee8_p6spy.entity.User;

public class UserIT {

private static EntityManager em;

private final Faker faker = new Faker(new Locale("sv-SE")); // default Locale("en", "")

@BeforeClass
public static void oneTimeSetUp() throws Exception {
em = Persistence.createEntityManagerFactory("IT").createEntityManager();
}

@Before
public void setUp() throws Exception {
em.getTransaction().begin();
}

@After
public void tearDown() {
em.getTransaction().commit();
}

@AfterClass
public static void oneTimeTearDown() throws Exception {
}

// ----------------------- Test Methods -----------------------

@Test
public void create() throws Exception {
User user = new User();
user.setName(faker.name().fullName());

em.persist(user);
System.out.println("Persisted " + user.getId());
}

}

And my @Entity class.

>package se.magnuskkarlsson.example.javaee8_p6spy.entity;

import java.util.Date;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;

@Entity
public class User {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Temporal(TemporalType.TIMESTAMP)
@Column
private Date created = new Date();

@NotBlank
@Size(min = 2, max = 255)
@Column(length = 255)
private String name;

// ----------------------- Logic Methods -----------------------

// ----------------------- Helper Methods -----------------------

@Override
public String toString() {
return "User [id=" + id + ", created=" + created + ", name=" + name + "]";
}

// ----------------------- Get and Set Methods -----------------------

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public Date getCreated() {
return created;
}

public void setCreated(Date created) {
this.created = created;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

}

And finally we need a P6Spy configuration file src/test/resources/spy.properties, which we copy from https://p6spy.readthedocs.io/en/latest/configandusage.html.

Then we need to uncomment one line to set logging to console appender=com.p6spy.engine.spy.appender.StdoutLogger.

>#################################################################
# P6Spy Options File #
# See documentation for detailed instructions #
# http://p6spy.github.io/p6spy/2.0/configandusage.html #
#################################################################

#################################################################
# MODULES #
# #
# Module list adapts the modular functionality of P6Spy. #
# Only modules listed are active. #
# (default is com.p6spy.engine.logging.P6LogFactory and #
# com.p6spy.engine.spy.P6SpyFactory) #
# Please note that the core module (P6SpyFactory) can't be #
# deactivated. #
# Unlike the other properties, activation of the changes on #
# this one requires reload. #
#################################################################
#modulelist=com.p6spy.engine.spy.P6SpyFactory,com.p6spy.engine.logging.P6LogFactory,com.p6spy.engine.outage.P6OutageFactory

################################################################
# CORE (P6SPY) PROPERTIES #
################################################################

# A comma separated list of JDBC drivers to load and register.
# (default is empty)
#
# Note: This is normally only needed when using P6Spy in an
# application server environment with a JNDI data source or when
# using a JDBC driver that does not implement the JDBC 4.0 API
# (specifically automatic registration).
#driverlist=

# for flushing per statement
# (default is false)
#autoflush=false

# sets the date format using Java's SimpleDateFormat routine.
# In case property is not set, milliseconds since 1.1.1970 (unix time) is used (default is empty)
#dateformat=

# prints a stack trace for every statement logged
#stacktrace=false
# if stacktrace=true, specifies the stack trace to print
#stacktraceclass=

# determines if property file should be reloaded
# Please note: reload means forgetting all the previously set
# settings (even those set during runtime - via JMX)
# and starting with the clean table
# (default is false)
#reloadproperties=false

# determines how often should be reloaded in seconds
# (default is 60)
#reloadpropertiesinterval=60

# specifies the appender to use for logging
# Please note: reload means forgetting all the previously set
# settings (even those set during runtime - via JMX)
# and starting with the clean table
# (only the properties read from the configuration file)
# (default is com.p6spy.engine.spy.appender.FileLogger)
#appender=com.p6spy.engine.spy.appender.Slf4JLogger
appender=com.p6spy.engine.spy.appender.StdoutLogger
#appender=com.p6spy.engine.spy.appender.FileLogger

# name of logfile to use, note Windows users should make sure to use forward slashes in their pathname (e:/test/spy.log)
# (used for com.p6spy.engine.spy.appender.FileLogger only)
# (default is spy.log)
#logfile=spy.log

# append to the p6spy log file. if this is set to false the
# log file is truncated every time. (file logger only)
# (default is true)
#append=true

# class to use for formatting log messages (default is: com.p6spy.engine.spy.appender.SingleLineFormat)
#logMessageFormat=com.p6spy.engine.spy.appender.SingleLineFormat

# Custom log message format used ONLY IF logMessageFormat is set to com.p6spy.engine.spy.appender.CustomLineFormat
# default is %(currentTime)|%(executionTime)|%(category)|connection%(connectionId)|%(sqlSingleLine)
# Available placeholders are:
# %(connectionId) the id of the connection
# %(currentTime) the current time expressing in milliseconds
# %(executionTime) the time in milliseconds that the operation took to complete
# %(category) the category of the operation
# %(effectiveSql) the SQL statement as submitted to the driver
# %(effectiveSqlSingleLine) the SQL statement as submitted to the driver, with all new lines removed
# %(sql) the SQL statement with all bind variables replaced with actual values
# %(sqlSingleLine) the SQL statement with all bind variables replaced with actual values, with all new lines removed
#customLogMessageFormat=%(currentTime)|%(executionTime)|%(category)|connection%(connectionId)|%(sqlSingleLine)

# format that is used for logging of the java.util.Date implementations (has to be compatible with java.text.SimpleDateFormat)
# (default is yyyy-MM-dd'T'HH:mm:ss.SSSZ)
#databaseDialectDateFormat=yyyy-MM-dd'T'HH:mm:ss.SSSZ

# format that is used for logging of the java.sql.Timestamp implementations (has to be compatible with java.text.SimpleDateFormat)
# (default is yyyy-MM-dd'T'HH:mm:ss.SSSZ)
#databaseDialectTimestampFormat=yyyy-MM-dd'T'HH:mm:ss.SSSZ

# format that is used for logging booleans, possible values: boolean, numeric
# (default is boolean)
#databaseDialectBooleanFormat=boolean

# Specifies the format for logging binary data. Not applicable if excludebinary is true.
# (default is com.p6spy.engine.logging.format.HexEncodedBinaryFormat)
#databaseDialectBinaryFormat=com.p6spy.engine.logging.format.PostgreSQLBinaryFormat
#databaseDialectBinaryFormat=com.p6spy.engine.logging.format.MySQLBinaryFormat
#databaseDialectBinaryFormat=com.p6spy.engine.logging.format.HexEncodedBinaryFormat

# whether to expose options via JMX or not
# (default is true)
#jmx=true

# if exposing options via jmx (see option: jmx), what should be the prefix used?
# jmx naming pattern constructed is: com.p6spy(.<jmxPrefix>)?:name=<optionsClassName>
# please note, if there is already such a name in use it would be unregistered first (the last registered wins)
# (default is none)
#jmxPrefix=

# if set to true, the execution time will be measured in nanoseconds as opposed to milliseconds
# (default is false)
#useNanoTime=false

#################################################################
# DataSource replacement #
# #
# Replace the real DataSource class in your application server #
# configuration with the name com.p6spy.engine.spy.P6DataSource #
# (that provides also connection pooling and xa support). #
# then add the JNDI name and class name of the real #
# DataSource here #
# #
# Values set in this item cannot be reloaded using the #
# reloadproperties variable. Once it is loaded, it remains #
# in memory until the application is restarted. #
# #
#################################################################
#realdatasource=/RealMySqlDS
#realdatasourceclass=com.mysql.jdbc.jdbc2.optional.MysqlDataSource

#################################################################
# DataSource properties #
# #
# If you are using the DataSource support to intercept calls #
# to a DataSource that requires properties for proper setup, #
# define those properties here. Use name value pairs, separate #
# the name and value with a semicolon, and separate the #
# pairs with commas. #
# #
# The example shown here is for mysql #
# #
#################################################################
#realdatasourceproperties=port;3306,serverName;myhost,databaseName;jbossdb,foo;bar

#################################################################
# JNDI DataSource lookup #
# #
# If you are using the DataSource support outside of an app #
# server, you will probably need to define the JNDI Context #
# environment. #
# #
# If the P6Spy code will be executing inside an app server then #
# do not use these properties, and the DataSource lookup will #
# use the naming context defined by the app server. #
# #
# The two standard elements of the naming environment are #
# jndicontextfactory and jndicontextproviderurl. If you need #
# additional elements, use the jndicontextcustom property. #
# You can define multiple properties in jndicontextcustom, #
# in name value pairs. Separate the name and value with a #
# semicolon, and separate the pairs with commas. #
# #
# The example shown here is for a standalone program running on #
# a machine that is also running JBoss, so the JNDI context #
# is configured for JBoss (3.0.4). #
# #
# (by default all these are empty) #
#################################################################
#jndicontextfactory=org.jnp.interfaces.NamingContextFactory
#jndicontextproviderurl=localhost:1099
#jndicontextcustom=java.naming.factory.url.pkgs;org.jboss.naming:org.jnp.interfaces

#jndicontextfactory=com.ibm.websphere.naming.WsnInitialContextFactory
#jndicontextproviderurl=iiop://localhost:900

################################################################
# P6 LOGGING SPECIFIC PROPERTIES #
################################################################

# filter what is logged
# please note this is a precondition for usage of: include/exclude/sqlexpression
# (default is false)
#filter=false

# comma separated list of strings to include
# please note that special characters escaping (used in java) has to be done for the provided regular expression
# (default is empty)
#include=
# comma separated list of strings to exclude
# (default is empty)
#exclude=

# sql expression to evaluate if using regex
# please note that special characters escaping (used in java) has to be done for the provided regular expression
# (default is empty)
#sqlexpression=

#list of categories to exclude: error, info, batch, debug, statement,
#commit, rollback, result and resultset are valid values
# (default is info,debug,result,resultset,batch)
#excludecategories=info,debug,result,resultset,batch

#whether the binary values (passed to DB or retrieved ones) should be logged with placeholder: [binary] or not.
# (default is false)
#excludebinary=false

# Execution threshold applies to the standard logging of P6Spy.
# While the standard logging logs out every statement
# regardless of its execution time, this feature puts a time
# condition on that logging. Only statements that have taken
# longer than the time specified (in milliseconds) will be
# logged. This way it is possible to see only statements that
# have exceeded some high water mark.
# This time is reloadable.
#
# executionThreshold=integer time (milliseconds)
# (default is 0)
#executionThreshold=

################################################################
# P6 OUTAGE SPECIFIC PROPERTIES #
################################################################
# Outage Detection
#
# This feature detects long-running statements that may be indicative of
# a database outage problem. If this feature is turned on, it will log any
# statement that surpasses the configurable time boundary during its execution.
# When this feature is enabled, no other statements are logged except the long
# running statements. The interval property is the boundary time set in seconds.
# For example, if this is set to 2, then any statement requiring at least 2
# seconds will be logged. Note that the same statement will continue to be logged
# for as long as it executes. So if the interval is set to 2, and the query takes
# 11 seconds, it will be logged 5 times (at the 2, 4, 6, 8, 10 second intervals).
#
# outagedetection=true|false
# outagedetectioninterval=integer time (seconds)
#
# (default is false)
#outagedetection=false
# (default is 60)
#outagedetectioninterval=30

Now lets run the integration test and we can now see P6Spy logging on one line SQL query, and parameter values. But we also see execution time, which is the second field.

>1605965528692|0|commit|connection 0|url jdbc:p6spy:h2:mem:||
1605965528694|0|statement|connection 0|url jdbc:p6spy:h2:mem:|drop table User if exists|drop table User if exists
...
1605965528697|0|commit|connection 0|url jdbc:p6spy:h2:mem:||
1605965528705|7|statement|connection 0|url jdbc:p6spy:h2:mem:|create table User (id bigint generated by default as identity, created timestamp, name varchar(255), primary key (id))|create table User (id bigint generated by default as identity, created timestamp, name varchar(255), primary key (id))
...
1605965529531|1|statement|connection 0|url jdbc:p6spy:h2:mem:|insert into User (id, created, name) values (null, ?, ?)|insert into User (id, created, name) values (null, '2020-11-21T14:32:09.480+0100', 'Karin Nilsson')
...
1605965529540|0|commit|connection 0|url jdbc:p6spy:h2:mem:||

Now lets build a simple JAX-RS app and use P6Spy and MySQL Community Edition in JBoss EAP 7.3. How To Install MySQL Community Edition (CE) 8.0 on Fedora 32/31/30/29

After installing MySQL 8 CE we need to configure MySQL DataSource in EAP 7. First create folder and file jboss-eap-7.3.0/modules/com/mysql/main/module.xml. Then copy MySQL CE 8 JDBC Driver from above Maven dependency mysql:mysql-connector-java:8.0.17 to this folder. [1]

><?xml version="1.0" ?>
<module xmlns="urn:jboss:module:1.1" name="com.mysql">
<resources>
<resource-root path="mysql-connector-java-8.0.17.jar" />
</resources>
<dependencies>
<module name="javax.api" />
<module name="javax.transaction.api" />
</dependencies>
</module>

Then we also is going to need to add P6Spy as a EAP/Wildfly module. Create folder and file jboss-eap-7.3.0/modules/com/p6spy/main/module.xml and copy P6Spy from above maven dependency p6spy:p6spy:3.9.1. Note that we also add EAP module org.slf4j and com.mysql, since in production we do not want to log to standard out, but use a logging framework. We are going to use standard jboss logging, but we need the SLF4J logging API bridge.

><?xml version="1.0" ?>
<module xmlns="urn:jboss:module:1.1" name="com.p6spy">
<resources>
<resource-root path="p6spy-3.9.1.jar" />
</resources>
<dependencies>
<module name="javax.api" />
<module name="javax.transaction.api" />
<module name="org.slf4j"/>
<module name="com.mysql"/>
</dependencies>
</module>

Then we going to run EAP in standalone mode, so lets edit standalone.xml. Note that we need to add MySQL and P6Spy driver and in datasoruce connection-url and driver.

><subsystem xmlns="urn:jboss:domain:datasources:5.0">
<datasources>
...
<datasource jndi-name="java:jboss/datasources/MySQLDS" pool-name="MySQLDS" enabled="true" use-java-context="true" statistics-enabled="${wildfly.datasources.statistics-enabled:${wildfly.statistics-enabled:false}}">
<connection-url>jdbc:p6spy:mysql://localhost:3306/p6spy?serverTimezone=UTC</connection-url>
<driver>p6spy</driver>
<security>
<user-name>root</user-name>
<password>root</password>
</security>
<validation>
<valid-connection-checker class-name="org.jboss.jca.adapters.jdbc.extensions.mysql.MySQLValidConnectionChecker"/>
<validate-on-match>true</validate-on-match>
<background-validation>false</background-validation>
<exception-sorter class-name="org.jboss.jca.adapters.jdbc.extensions.mysql.MySQLExceptionSorter"/>
</validation>
</datasource>
<drivers>
...
<driver name="mysql" module="com.mysql">
<driver-class>com.mysql.cj.jdbc.Driver</driver-class>
<xa-datasource-class>com.mysql.cj.jdbc.MysqlXADataSource</xa-datasource-class>
</driver>
<driver name="p6spy" module="com.p6spy">
<driver-class>com.p6spy.engine.spy.P6SpyDriver</driver-class>
</driver>
</drivers>
</datasources>
</subsystem>

The final thing to do is to copy jboss-eap-7.3.0/bin/spy.properties. And change

>appender=com.p6spy.engine.spy.appender.Slf4JLogger
#appender=com.p6spy.engine.spy.appender.StdoutLogger
#appender=com.p6spy.engine.spy.appender.FileLogger

Now when we run this and invoke our simple JAX-RS service from curl.

>$ curl -v -X POST -H "Content-Type: application/json" -H "Accept: application/json" --data '{"name":"MY_NAME"}' http://localhost:8080/javaee8-p6spy/api/users

We see in server.log

>2020-11-19 02:45:06,446 INFO  [p6spy] (ServerService Thread Pool -- 82) 1605750306446|0|statement|connection 0|url jdbc:p6spy:mysql://localhost:3306/p6spy?serverTimezone=UTC|SELECT 1|SELECT 1
...
2020-11-19 02:45:06,497 INFO [p6spy] (ServerService Thread Pool -- 82) 1605750306497|0|statement|connection 0|url jdbc:p6spy:mysql://localhost:3306/p6spy?serverTimezone=UTC|SELECT 1|SELECT 1
...
2020-11-19 02:45:06,867 INFO [se.magnuskkarlsson.example.javaee8_p6spy.boundary.UserResource] (default task-1) Creating User [id=null, created=Thu Nov 19 02:45:06 CET 2020, name=MY_NAME] ...
2020-11-19 02:45:06,870 INFO [p6spy] (default task-1) 1605750306870|0|statement|connection 0|url jdbc:p6spy:mysql://localhost:3306/p6spy?serverTimezone=UTC|SELECT 1|SELECT 1
2020-11-19 02:45:06,873 INFO [p6spy] (default task-1) 1605750306873|0|statement|connection 0|url jdbc:p6spy:mysql://localhost:3306/p6spy?serverTimezone=UTC|insert into User (created, name) values (?, ?)|insert into User (created, name) values ('2020-11-19T02:45:06.816+0100', 'MY_NAME')
2020-11-19 02:45:06,874 INFO [se.magnuskkarlsson.example.javaee8_p6spy.boundary.UserResource] (default task-1) Created User [id=5, created=Thu Nov 19 02:45:06 CET 2020, name=MY_NAME].
2020-11-19 02:45:06,895 INFO [p6spy] (default task-1) 1605750306895|9|commit|connection 0|url jdbc:p6spy:mysql://localhost:3306/p6spy?serverTimezone=UTC||
...

Reference:

JSON-P in JavaEE 7

$
0
0

JSON-P can be used to parse and stringify JSON data.

><dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>8.0</version>
<scope>provided</scope>
</dependency>

<!-- JSON-P Impl -->
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>jakarta.json</artifactId>
<version>1.1.6</version>
<scope>test</scope>
</dependency>
>package se.magnuskkarlsson.example.javaee8_p6spy.control;

import java.io.StringReader;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Date;

import javax.json.Json;
import javax.json.JsonObject;

import org.junit.Test;

public class JSONPTest {

public static final String DATETIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSZ";

@Test
public void stringify() throws Exception {
// https://javaee.github.io/jsonp/
JsonObject json = Json.createObjectBuilder() //
.add("name", "Falco") //
// "JSON does not have a built-in type for date/time values. The general consensus is to store the
// date/time value as a string in ISO 8601 format."
// https://docs.jsonata.org/date-time#json-and-iso-8601
.add("created", new SimpleDateFormat(DATETIME_FORMAT).format(new Date())) //
.add("age", BigDecimal.valueOf(3)) //
.add("emails", Json.createArrayBuilder() //
.add("foo@domain.com") //
.add("boo@domain.se")) //
.add("biteable", Boolean.FALSE) //
.build();
String result = json.toString();

System.out.println(result);
}

@Test
public void parse() throws Exception {
// https://javaee.github.io/jsonp/getting-started.html
String json = "{\"name\":\"John Doe\",\"created\":\"2001-07-04T12:08:56.235-0700\",\"age\":28,\"bitable\":true}";
JsonObject obj = Json.createReader(new StringReader(json)).readObject();
System.out.println(obj);
System.out.println(obj.getString("name"));
System.out.println(obj.getString("created"));
System.out.println(obj.getInt("age"));
System.out.println(obj.getBoolean("bitable"));
}

}

JSON Binding (JSON-B) in Java EE 8

$
0
0

Instead of getting and adding single properties on JsonObjectBuilder or JsonObject, an entire Java Object could be serialized and deserialized.


<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>8.0</version>
<scope>provided</scope>
</dependency>

<!-- JSON-B 1.1 Impl -->
<dependency>
<groupId>org.eclipse</groupId>
<artifactId>yasson</artifactId>
<version>1.0.5</version>
<scope>test</scope>
</dependency>
>package se.magnuskkarlsson.example.javaee8_p6spy.control;

import java.util.Locale;

import javax.json.bind.JsonbBuilder;

import org.junit.Test;

import com.github.javafaker.Faker;

import se.magnuskkarlsson.example.javaee8_p6spy.entity.Address;
import se.magnuskkarlsson.example.javaee8_p6spy.entity.User;

public class JSONBTest {

private final Faker faker = new Faker(new Locale("sv-SE")); // default Locale("en", "")

@Test
public void serialize() throws Exception {
User user = new User();
user.setName(faker.name().fullName());

Address address1 = new Address();
address1.setStreet(faker.address().streetAddress());
user.addAddress(address1);

Address address2 = new Address();
address2.setStreet(faker.address().streetAddress());
user.addAddress(address2);

// https://javaee.github.io/jsonb-spec/
String result = JsonbBuilder.create().toJson(user);

System.out.println(result);
}

}

And the POJO classes.

>package se.magnuskkarlsson.example.javaee8_p6spy.entity;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;

@Entity
public class User {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Temporal(TemporalType.TIMESTAMP)
@Column
private Date created = new Date();

@Column(columnDefinition = "BIT")
private Boolean enabled = Boolean.TRUE;

@NotBlank
@Size(min = 2, max = 255)
@Column(length = 255)
private String name;

// https://vladmihalcea.com/the-best-way-to-map-a-onetomany-association-with-jpa-and-hibernate/
// Next best bidirectional @OneToMany.
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Address> addresses = new ArrayList<>();

// ----------------------- Logic Methods -----------------------

public void addAddress(Address address) {
addresses.add(address);
address.setUser(this);
}

public void removeAddress(Address address) {
addresses.remove(address);
address.setUser(null);
}

// ----------------------- Helper Methods -----------------------

@Override
public String toString() {
return "User [id=" + id + ", created=" + created + ", enabled=" + enabled + ", name=" + name + ", addresses="
+ addresses + "]";
}

// ----------------------- Get and Set Methods -----------------------

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public Date getCreated() {
return created;
}

public void setCreated(Date created) {
this.created = created;
}

public Boolean getEnabled() {
return enabled;
}

public void setEnabled(Boolean enabled) {
this.enabled = enabled;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public List<Address> getAddresses() {
return addresses;
}

public void setAddresses(List<Address> addresses) {
this.addresses = addresses;
}

}
>package se.magnuskkarlsson.example.javaee8_p6spy.entity;

import javax.json.bind.annotation.JsonbTransient;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;

@Entity
public class Address {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@NotBlank
@Size(min = 2, max = 255)
@Column(length = 255)
private String street;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
// https://javaee.github.io/tutorial/jsonb002.html
@JsonbTransient
private User user;

// ----------------------- Logic Methods -----------------------

// ----------------------- Helper Methods -----------------------

@Override
public String toString() {
return "Address [id=" + id + ", street=" + street + "]";
}

// Note equals and hashCode are important to implement when working with detached objects.
// https://vladmihalcea.com/the-best-way-to-implement-equals-hashcode-and-tostring-with-jpa-and-hibernate/
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof Address)) {
return false;
}
return id != null && id.equals(((Address) o).getId());
}

// Note equals and hashCode are important to implement when working with detached objects.
@Override
public int hashCode() {
// Database-generated identifiers
// https://vladmihalcea.com/how-to-implement-equals-and-hashcode-using-the-jpa-entity-identifier/
return 31;
}

// ----------------------- Get and Set Methods -----------------------

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getStreet() {
return street;
}

public void setStreet(String street) {
this.street = street;
}

public User getUser() {
return user;
}

public void setUser(User user) {
this.user = user;
}

}

And when run

>{"addresses":[{"street":"Hjördiss Väg 79"},{"street":"Granallén 27"}],"created":"2020-11-21T19:32:33.302Z[UTC]","enabled":true,"name":"Maria Änglund"}

Bootstrap Admin Dashboard

$
0
0

How to Create Tor Browser Desktop Shortcut in Fedora 31

$
0
0

You can download Tor Web Browser from https://www.torproject.org/download/.

Unzip to personal folder, e.g. ~/bin. You will find in unzipped Tor directory a .desktop file. To add that desktop file to GNOME meny, you copy that file to applications.

$ cp ~/bin/tor-browser-linux64-10.0.4_en-US/tor-browser_en-US/start-tor-browser.desktop ~/.local/share/applications/start-tor-browser-linux64-10.0.4_en-US.desktop
$ chmod +x ~/.local/share/applications/start-tor-browser-linux64-10.0.4_en-US.desktop

Reference:https://developer.gnome.org/integration-guide/stable/desktop-files.html.en

How to Best Map Object Inheritance in JPA/Hibernate

$
0
0

Introduction

Object inheritance can be mapped in 3 ways in JPA/Hibernate.

  • SINGLE_TABLE
  • TABLE_PER_CLASS
  • JOINED

SINGLE_TABLE - @Inheritance(strategy = InheritanceType.SINGLE_TABLE)

All Inheritance Java Classes are stored in one big database table.

Default @Inheritance(strategy = InheritanceType.SINGLE_TABLE)

pros

  • Faster query, since no joining of tables

cons

  • Data Integrity. Cannot uphold NOT NULL constraints, etc, but can be enforced via a CHECK of TRIGGER. Bean Validation can improve, but does not help if either integrate directly with the database.
  • Works when smaller set of different inhereted class, but single table grows

TABLE_PER_CLASS - @MappedSuperclass

https://vladmihalcea.com/how-to-inherit-properties-from-a-base-class-entity-using-mappedsuperclass-with-jpa-and-hibernate/

pros

  • Can enforce NOT NULL constraints, etc

cons

  • You rarely need to joind inhereted java class, so no real cons

JOINED - @Inheritance(strategy = InheritanceType.JOINED)

https://vladmihalcea.com/the-best-way-to-use-entity-inheritance-with-jpa-and-hibernate/

Do no use, no efficient SQL.

Summary

Use TABLE_PER_CLASS with @MappedSuperclass.

Hot Deployment of Classes with Eclipse JBoss Plugin Red Hat CodeReady Studio

$
0
0

Install Eclipse Plugin Red Hat CodeReady Studio

Easiest alternative is to do it when online. Follow instruction at https://access.redhat.com/documentation/en-us/red_hat_codeready_studio/12.18/html-single/installation_guide/index#installing-crs-in-eclipse-when-online_installing-crs-in-eclipse.

Configure Hot Deployment Scanner in JBoss EAP 7.3

Get JBoss EAP 7.3 zip and extract it. Then edit standalone.xml and set auto-deploy-exploded="true".

><subsystem xmlns="urn:jboss:domain:deployment-scanner:2.0">
<deployment-scanner path="deployments" relative-to="jboss.server.base.dir" scan-interval="5000" auto-deploy-exploded="true" runtime-failure-causes-rollback="${jboss.deployment.scanner.rollback.on.failure:false}"/>
</subsystem>

Run/Debug

Finally configure JBoss EAP 7.3 server in server window.


How to Install Visual Studio Code on Fedora 33

$
0
0

Prerequisite

>$ cat /etc/fedora-release 
Fedora release 33 (Thirty Three)

Installation

Reference: https://code.visualstudio.com/docs/setup/linux

>$ sudo rpm --import https://packages.microsoft.com/keys/microsoft.asc

$ sudo sh -c 'echo -e "[code]\nname=Visual Studio Code\nbaseurl=https://packages.microsoft.com/yumrepos/vscode\nenabled=1\ngpgcheck=1\ngpgkey=https://packages.microsoft.com/keys/microsoft.asc"> /etc/yum.repos.d/vscode.repo'

$ sudo dnf check-update; sudo dnf install code

Introduction to Protocol Buffers, protobuf

$
0
0

What is Protocol Buffers, protobuf

"Protocol buffers are Google's language-neutral, platform-neutral, extensible mechanism for serializing structured data – think XML, but smaller, faster, and simpler."https://developers.google.com/protocol-buffers

Advantages

  • Data is binary (more efficient bandwidth)
  • Custom parser for binary data (more CPU efficient, compared with JSON)
  • Data is typed.
  • Wide variety of supported language (Java, C#, Go, Python, JavaScript).
  • Schema based (.proto)

Disadvantages

  • Since data is sent in binary format, you cannot read it as plaintext or JSON.
  • Some language is not supported.

Protocol Buffers, protobuf was deloped by Google and is used for almost all their internal applications.

Data Types (Scalar Value Types)

https://developers.google.com/protocol-buffers/docs/proto3#scalar

  • Integer numbers: int32, int64
  • Decimal numbers: float (32), double (64)
  • Boolean (True or False): bool
  • String: string (String must always be encoded UTF-8 or 7-bit ASCII (US))
  • Byte Array: bytes

Schema .proto file

"Files should be named lower_snake_case.proto"https://developers.google.com/protocol-buffers/docs/style

Message Type

In protobuf we define Messages, e.g. 'message Person'

Field Type, e.g. string

Field Name, e.g. first_name

Field Tag (order), e.g. '= 1;'

>// The syntax for this file is proto3
syntax = "proto3";

/* Person is used to identity
* across our system. */
message Person {
int32 age = 1;
string first_name = 2;
string last_name = 3;
bytes picture = 4;
bool is_profile_verified = 5;
float height = 6;

// array/list
repeated string phone_numbers = 7;

enum EyeColor {
// first enum is always the default
EYE_COLOR_UNSPECIFIED = 0;
EYE_COLOR_GREEN = 1;
EYE_COLOR_BROWN = 2;
EYE_COLOR_BLUE = 3;
}

EyeColor eye_color = 8;
}

Style Guide

https://developers.google.com/protocol-buffers/docs/style

  • "Keep the line length to 80 characters."
  • "Use an indent of 2 spaces."
  • "Package name should be in lowercase, and should correspond to the directory hierarchy. e.g., if a file is in my/package/, then the package name should be my.package."
  • "Use CamelCase (with an initial capital) for message names"
  • "Use underscore_separated_names for field names (including oneof field and extension names) – for example, song_name."
  • "Use pluralized names for repeated fields."
  • "Use CamelCase (with an initial capital) for enum type names and CAPITALS_WITH_UNDERSCORES for value names:"
  • "If your .proto defines an RPC service, you should use CamelCase (with an initial capital) for both the service name and any RPC method names"

Default Values for Fields

All fields, if not specified, will have default value.

  • bool: false
  • number (int32, etc): 0
  • string: empty string
  • bytes: empty byte array/list
  • enum: first value
  • repeated: empty array/list

Protocol Buffers (protobuf) Compiler/Code Generator - protoc

$
0
0

Introduction

In Protocol Buffers (protobuf) is API described in a .proto file, next step is to generate language specific code for that API. That is done with Protocol Buffer Compiler (protoc). Here we will generate Java Code, but there are many language supported with protobuf.

Graddle

The official supported Java plugin from Google (the creator of profobuf) is gradle https://github.com/google/protobuf-gradle-plugin. But changing build tool might not suite your overall CI/DI infrastructure, so here I will show Maven plugin.

Maven

https://www.xolstice.org/protobuf-maven-plugin/

Install Protocol Buffer Compiler (protoc)

On Fedora 33 ($ cat /etc/fedora-release) it is easy to install protoc.

>$ sudo dnf install protobuf-compiler

protobuf-maven-plugin

https://www.xolstice.org/protobuf-maven-plugin/

.proto files are stored in src/main/proto. Lets create a simple .proto file

>syntax = "proto3";

package se.magnuskkarlsson.example_protobuf;

message SimpleMessage {
int32 id = 1;
bool is_simple = 2;
string name = 3;
repeated int32 sample_list = 4;
}

And pom.xml

><?xml version="1.0" encoding="UTF-8"?>
<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>se.magnuskkarlsson</groupId>
<artifactId>example-protobuf</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>war</packaging>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.build.outputEncoding>UTF-8</project.build.outputEncoding>
<failOnMissingWebXml>false</failOnMissingWebXml>
</properties>

<dependencies>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>3.15.6</version>
</dependency>

<!-- Test Support -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.10.19</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<finalName>${project.artifactId}</finalName>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<release>11</release>
<showDeprecation>true</showDeprecation>
<showWarnings>true</showWarnings>
</configuration>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>0.6.1</version>
<configuration>
<protocExecutable>/usr/bin/protoc</protocExecutable>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

Then build with maven: '$ mvn clean install'. Generated source code is stored in target/generated-sources/protobuf/java

Now lets write a simple a class to test our new generated code.

>package se.magnuskkarlsson.example_protobuf;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Arrays;

import se.magnuskkarlsson.example_protobuf.Message.SimpleMessage;

public class Main {

public static void main(String[] args) throws FileNotFoundException, IOException {
SimpleMessage message = SimpleMessage.newBuilder() //
.setId(42) //
.setIsSimple(false) //
.setName("FOO") //
.addAllSampleList(Arrays.asList(3, 4, 5)) //
.build();
System.out.println(message);

File file = File.createTempFile("message", ".bin");
try (FileOutputStream fos = new FileOutputStream(file)) {
message.writeTo(fos);
}
System.out.println("Wrote " + file.getAbsolutePath());
}

}

Comparison HTTP/1.1 and HTTP/2

$
0
0

HTTP 1.1 was released 1997 and  

  • Was originally composed for HTTP GET and POST.
  • Works with Request/Response and not server push
  • Do not compress HTTP headers. Today are many HTTP headers used.
  • Opens a new TCP connection to a server at each request

 
HTTP/2 was released in 2015
 

  • Supports multiplexing. Client and server can push messages in parallel over the same TCP connection, which reduces latency.
  • Supports server push streams (multiple messages) for one request from the client.
  • Supports header compression.

Reference: https://developers.google.com/web/fundamentals/performance/http2

Overvew gRPC (Remote Procedure Call)

$
0
0

Introduction

"gRPC is a modern open source high performance Remote Procedure Call (RPC) framework that can run in any environment. It can efficiently connect services in and across data centers with pluggable support for load balancing, tracing, health checking and authentication. It is also applicable in last mile of distributed computing to connect devices, mobile applications and browsers to backend services."https://grpc.io/

4 Types of API in gRPC

https://medium.com/@yangli907/grpc-learning-part-1-cdcf59e52707

API protobuf

src/main/proto/example_grpc/hello_service.proto

>syntax = "proto3";

// "Package name should be in lowercase, and should correspond
// to the directory hierarchy. e.g., if a file is in my/package/,
// then the package name should be my.package."
// [https://developers.google.com/protocol-buffers/docs/style]
package example_grpc;

service HelloService {

// Unary RPC
rpc Hello(HelloRequest) returns (HelloResponse) {};

// Server Streaming RPC
rpc HelloServerStreaming(HelloServerStreamingRequest) returns (stream HelloServerStreamingResponse) {};

// Client Streaming RPC
rpc HelloClientStreaming(stream HelloClientStreamingRequest) returns (HelloClientStreamingResponse) {};

// Bidirectional Streaming RPC
rpc HelloBiDirectionalStreaming(stream HelloBiDirectionalStreamingRequest) returns (stream HelloBiDirectionalStreamingResponse) {};

}

message HelloRequest {
string name = 1;
}

message HelloResponse {
string message = 1;
}

message HelloServerStreamingRequest {
string name = 1;
}

message HelloServerStreamingResponse {
string message = 1;
}

message HelloClientStreamingRequest {
string name = 1;
}

message HelloClientStreamingResponse {
string message = 1;
}

message HelloBiDirectionalStreamingRequest {
string name = 1;
}

message HelloBiDirectionalStreamingResponse {
string message = 1;
}

pom.xml

><?xml version="1.0" encoding="UTF-8"?>
<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>se.magnuskkarlsson</groupId>
<artifactId>example-grpc</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.build.outputEncoding>UTF-8</project.build.outputEncoding>
<failOnMissingWebXml>false</failOnMissingWebXml>
</properties>

<dependencies>
<!-- https://github.com/grpc/grpc-java Chapter: Download -->
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-netty-shaded</artifactId>
<version>1.36.0</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-protobuf</artifactId>
<version>1.36.0</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-stub</artifactId>
<version>1.36.0</version>
</dependency>
<dependency> <!-- necessary for Java 9+ -->
<groupId>org.apache.tomcat</groupId>
<artifactId>annotations-api</artifactId>
<version>6.0.53</version>
<scope>provided</scope>
</dependency>

<!-- Test Support -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.10.19</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<!-- https://github.com/grpc/grpc-java Chapter: Generated Code -->
<extensions>
<extension>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>1.6.2</version>
</extension>
</extensions>
<finalName>${project.artifactId}</finalName>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<release>11</release>
<showDeprecation>true</showDeprecation>
<showWarnings>true</showWarnings>
</configuration>
</plugin>
</plugins>
</pluginManagement>
<plugins>

<!-- https://github.com/grpc/grpc-java Chapter: Generated Code -->
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>0.6.1</version>
<configuration>
<protocArtifact>com.google.protobuf:protoc:3.12.0:exe:${os.detected.classifier}</protocArtifact>
<pluginId>grpc-java</pluginId>
<pluginArtifact>io.grpc:protoc-gen-grpc-java:1.36.0:exe:${os.detected.classifier}</pluginArtifact>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>compile-custom</goal>
</goals>
</execution>
</executions>
</plugin>

<!-- <plugin> <groupId>org.xolstice.maven.plugins</groupId> <artifactId>protobuf-maven-plugin</artifactId> <version>0.6.1</version>
<configuration> <protocExecutable>/usr/bin/protoc</protocExecutable> </configuration> <executions> <execution> <goals> <goal>compile</goal>
</goals> </execution> </executions> </plugin> -->
</plugins>
</build>
</project>

gRPC Server

A typical gRPC Server is created and started as a standalone server.

Below is the boilerplate code for a standalone server.

>package se.magnuskkarlsson.example_grpc.server;

import java.io.IOException;

import io.grpc.Server;
import io.grpc.ServerBuilder;

public class HelloServer {

public static void main(String[] args) throws IOException, InterruptedException {
System.out.println("Starting Server ...");

final Server server = ServerBuilder //
.forPort(50051) //
.addService(new HelloServiceImpl()) //
.build();

server.start();

Runtime.getRuntime().addShutdownHook(new Thread(() -> {
System.out.println("Shutting down Server ....");
server.shutdown();
System.out.println("Successfully stopped Server");
}));

server.awaitTermination();
}

}

gRPC Client

Below is the boilerplate code for a standalone client.

>package se.magnuskkarlsson.example_grpc.client;

import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.stub.StreamObserver;

// https://github.com/grpc/grpc-java/blob/master/examples/src/main/java/io/grpc/examples/routeguide/RouteGuideClient.java
public class HelloClient {

public static void main(String[] args) throws Exception {
System.out.println("Starting Client ...");
ManagedChannel channel = ManagedChannelBuilder //
.forAddress("localhost", 50051) //
.usePlaintext() //
.build();

HelloServiceBlockingStub syncClient = HelloServiceGrpc.newBlockingStub(channel);

System.out.println("Shutting down Client");
channel.shutdown();
}

Unary RPC

Unary RPC is the most common call, it is the classical Request and Response pattern.

Server

>package se.magnuskkarlsson.example_grpc.server;

import java.util.ArrayList;
import java.util.List;

import example_grpc.HelloServiceGrpc.HelloServiceImplBase;
import example_grpc.HelloServiceOuterClass.HelloRequest;
import example_grpc.HelloServiceOuterClass.HelloResponse;
import io.grpc.stub.StreamObserver;

public class HelloServiceImpl extends HelloServiceImplBase {

// Unary RPC
@Override
public void hello(HelloRequest request, StreamObserver<HelloResponse> responseObserver) {
String name = request.getName();
System.out.println("[Unary] Hello name=" + name + " ...");

HelloResponse resp = HelloResponse.newBuilder() //
.setMessage("Hello from Async " + name) //
.build();

// send the response
responseObserver.onNext(resp);

responseObserver.onCompleted();
}

}

Client - Blocking Client; HelloServiceBlockingStub syncClient = HelloServiceGrpc.newBlockingStub(channel);

>package se.magnuskkarlsson.example_grpc.client;

import java.util.Iterator;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

import example_grpc.HelloServiceGrpc;
import example_grpc.HelloServiceGrpc.HelloServiceBlockingStub;
import example_grpc.HelloServiceGrpc.HelloServiceStub;
import example_grpc.HelloServiceOuterClass.HelloRequest;
import example_grpc.HelloServiceOuterClass.HelloResponse;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.stub.StreamObserver;

// https://github.com/grpc/grpc-java/blob/master/examples/src/main/java/io/grpc/examples/routeguide/RouteGuideClient.java
public class HelloClient {

public static void main(String[] args) throws Exception {
System.out.println("Starting Client ...");
ManagedChannel channel = ManagedChannelBuilder //
.forAddress("localhost", 50051) //
.usePlaintext() //
.build();

new HelloClient().unary(channel);

System.out.println("Shutting down Client");
channel.shutdown();
}

private void unary(ManagedChannel channel) {
HelloServiceBlockingStub syncClient = HelloServiceGrpc.newBlockingStub(channel);
// HelloServiceStub asyncClient = HelloServiceGrpc.newStub(channel);

HelloRequest req1 = HelloRequest.newBuilder().setName("Magnus").build();
HelloResponse resp1 = syncClient.hello(req1);
System.out.println("[Async] " + resp1);

HelloRequest req2 = HelloRequest.newBuilder().setName("John Doe").build();
HelloResponse resp2 = syncClient.hello(req2);
System.out.println("[Async] " + resp2);
}

}

Server Streaming RPC

Streaming Server is well suited for when Server needs to PUSH data to client. This is especially true for Big Data.

Server

>    // Server Streaming RPC
@Override
public void helloServerStreaming(HelloServerStreamingRequest request,
StreamObserver<HelloServerStreamingResponse> responseObserver) {

String name = request.getName();
System.out.println("[Server Streaming] Hello name=" + name + " ...");

try {
for (int i = 0; i < 10; ++i) {

HelloServerStreamingResponse resp = HelloServerStreamingResponse.newBuilder() //
.setMessage("Hello from Server Streaming " + name + "" + i) //
.build();
responseObserver.onNext(resp);

Thread.sleep(1000L);
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
responseObserver.onCompleted();
}
}

Blocking Client

>    private void serverStreaming(ManagedChannel channel) {
HelloServiceBlockingStub syncClient = HelloServiceGrpc.newBlockingStub(channel);
// HelloServiceStub asyncClient = HelloServiceGrpc.newStub(channel);

HelloServerStreamingRequest req1 = HelloServerStreamingRequest.newBuilder().setName("Magnus").build();
Iterator<HelloServerStreamingResponse> respIter = syncClient.helloServerStreaming(req1);
respIter.forEachRemaining(resp -> {
System.out.println("[Server Streaming]" + resp.getMessage());
});
}

Client Streaming RPC

Client Streaming is suited when the client wants to send Big Data. Or when server processing is possible to do in parallell, then can client split jobs.

Server

>    // Client Streaming RPC
@Override
public StreamObserver<HelloClientStreamingRequest> helloClientStreaming(
StreamObserver<HelloClientStreamingResponse> responseObserver) {

StreamObserver<HelloClientStreamingRequest> requestObserver = new StreamObserver<HelloClientStreamingRequest>() {

private List<String> names = new ArrayList<String>();

@Override
public void onNext(HelloClientStreamingRequest request) {
// client sends a message
String name = request.getName();
names.add(name);
}

@Override
public void onError(Throwable t) {
// client sends a error
}

@Override
public void onCompleted() {
// client is done
HelloClientStreamingResponse response = HelloClientStreamingResponse.newBuilder() //
.setMessage("[Client Streaming] Hello " + names.toString()) //
.build();
responseObserver.onNext(response);
responseObserver.onCompleted();
}

};

return requestObserver;
}

Asynch Client

>    private void clientStreaming(ManagedChannel channel) throws InterruptedException {
HelloServiceStub asyncClient = HelloServiceGrpc.newStub(channel);

CountDownLatch latch = new CountDownLatch(1);

StreamObserver<HelloClientStreamingResponse> responseObserver = new StreamObserver<HelloClientStreamingResponse>() {

@Override
public void onNext(HelloClientStreamingResponse response) {
// we get a response from the server
String message = response.getMessage();
System.out.println("[Client Streaming] Recieved message from server " + message);

// onNext will be called only once
}

@Override
public void onError(Throwable t) {
// we get an error from server
latch.countDown();
}

@Override
public void onCompleted() {
// the server is done sending us data

// onComplete will be called right after onNext()
System.out.println("[Client Streaming] Server has completed sending us data/stream");
latch.countDown();
}

};

StreamObserver<HelloClientStreamingRequest> requestObserver = asyncClient
.helloClientStreaming(responseObserver);

HelloClientStreamingRequest request1 = HelloClientStreamingRequest.newBuilder() //
.setName("John") //
.build();

requestObserver.onNext(request1);

HelloClientStreamingRequest request2 = HelloClientStreamingRequest.newBuilder() //
.setName("Nisse") //
.build();

requestObserver.onNext(request2);

HelloClientStreamingRequest request3 = HelloClientStreamingRequest.newBuilder() //
.setName("Klara") //
.build();

requestObserver.onNext(request3);

// tell the server, the client is done sending data/streaming
requestObserver.onCompleted();

latch.await(5, TimeUnit.SECONDS);
}

Bidirectional Streaming RPC

Bidirectional Streaming is like a chat protocol and have typical a long running connection.

Server

>    // Bidirectional streaming RPC
@Override
public StreamObserver<HelloBiDirectionalStreamingRequest> helloBiDirectionalStreaming(
StreamObserver<HelloBiDirectionalStreamingResponse> responseObserver) {

StreamObserver<HelloBiDirectionalStreamingRequest> requestObserver = new StreamObserver<HelloBiDirectionalStreamingRequest>() {

@Override
public void onNext(HelloBiDirectionalStreamingRequest request) {
// client sends a message
String name = request.getName();

HelloBiDirectionalStreamingResponse response = HelloBiDirectionalStreamingResponse.newBuilder() //
.setMessage("[Bi-Directional] Hello " + name) //
.build();
responseObserver.onNext(response);
}

@Override
public void onError(Throwable t) {
// client sends a error
}

@Override
public void onCompleted() {
// client is done sending data/stream
responseObserver.onCompleted();
}

};

return requestObserver;
}

Asynch Client

>    private void biDirectionalStreaming(ManagedChannel channel) throws InterruptedException {
HelloServiceStub asyncClient = HelloServiceGrpc.newStub(channel);

CountDownLatch latch = new CountDownLatch(1);

StreamObserver<HelloBiDirectionalStreamingResponse> responseObserver = new StreamObserver<HelloBiDirectionalStreamingResponse>() {

@Override
public void onNext(HelloBiDirectionalStreamingResponse response) {
// we get a response from the server
String message = response.getMessage();
System.out.println("[Bi-Directional Streaming] Recieved message from server " + message);
}

@Override
public void onError(Throwable t) {
// we get an error from server
latch.countDown();
}

@Override
public void onCompleted() {
// the server is done sending/streaming us data
System.out.println("[Bi-Directional Streaming] Server has completed sending us data/stream");
latch.countDown();
}

};

StreamObserver<HelloBiDirectionalStreamingRequest> requestObserver = asyncClient
.helloBiDirectionalStreaming(responseObserver);

HelloBiDirectionalStreamingRequest request1 = HelloBiDirectionalStreamingRequest.newBuilder() //
.setName("John") //
.build();
requestObserver.onNext(request1);
System.out.println("[Bi-Directional Streaming] Sending request1");

HelloBiDirectionalStreamingRequest request2 = HelloBiDirectionalStreamingRequest.newBuilder() //
.setName("Nisse") //
.build();
requestObserver.onNext(request2);
System.out.println("[Bi-Directional Streaming] Sending request2");

HelloBiDirectionalStreamingRequest request3 = HelloBiDirectionalStreamingRequest.newBuilder() //
.setName("Bertil") //
.build();
requestObserver.onNext(request3);
System.out.println("[Bi-Directional Streaming] Sending request3");

// tell the server, the client is done sending data/streaming
requestObserver.onCompleted();

latch.await(5, TimeUnit.SECONDS);
}

gRPC and SSL/TLS Ciphers and Protocol Version

$
0
0

Documentation

https://grpc.io/docs/guides/auth/

https://github.com/grpc/grpc-java/blob/master/SECURITY.md

Setup

Generate a self-signed server certificate in PEM format.

>$ keytool -genkeypair -alias localhost -keyalg RSA -keysize 2048 -dname "CN=localhost,O=Antigo,ST=Stockholm,C=SE" -validity 720 -storetype PKCS12 -keystore localhost.p12 -storepass changeit -keypass changeit -v
Generating 2,048 bit RSA key pair and self-signed certificate (SHA256withRSA) with a validity of 720 days
for: CN=localhost, O=Antigo, ST=Stockholm, C=SE
[Storing localhost.p12]

$ openssl pkcs12 -in localhost.p12 -out localhost.pem -nodes
Enter Import Password:

Then manually split pem file into a .crt.pem and .key.pem file.

Java

Server

>package se.magnuskkarlsson.example_grpc.server;

import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;

import io.grpc.Server;
import io.grpc.netty.shaded.io.grpc.netty.GrpcSslContexts;
import io.grpc.netty.shaded.io.grpc.netty.NettyServerBuilder;

public class HelloServer {

public static void main(String[] args) throws IOException, InterruptedException {
System.out.println("Starting Server ...");

InputStream certChainFile = HelloServer.class.getClassLoader().getResourceAsStream("localhost.crt.pem");
InputStream privateKeyFile = HelloServer.class.getClassLoader().getResourceAsStream("localhost.key.pem");
// final Server server = ServerBuilder //
// .forPort(50051) //
//// https://grpc.io/docs/guides/auth/#java
// .useTransportSecurity(certChainFile, privateKeyFile) //
// .addService(new HelloServiceImpl()) //
// .build();

Listlt;Stringgt; ciphers = Arrays.asList(/* "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384", */
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", /* "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256", */
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256");

// https://github.com/grpc/grpc-java/blob/master/SECURITY.md
final Server server = NettyServerBuilder //
.forPort(50051) //
.sslContext( //
GrpcSslContexts //
.forServer(certChainFile, privateKeyFile) //
.protocols("TLSv1.2") //
.ciphers(ciphers)
// .trustManager(clientCAsFile) //
// .clientAuth(ClientAuth.REQUIRE) //
.build()) //
.addService(new HelloServiceImpl()) //
.build();

server.start();

Runtime.getRuntime().addShutdownHook(new Thread(() -gt; {
System.out.println("Shutting down Server ....");
server.shutdown();
System.out.println("Successfully stopped Server");
}));

server.awaitTermination();
}

}


Client



>package se.magnuskkarlsson.example_grpc.client;

import java.io.InputStream;
import java.util.Iterator;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

import example_grpc.HelloServiceGrpc;
import example_grpc.HelloServiceGrpc.HelloServiceBlockingStub;
import example_grpc.HelloServiceGrpc.HelloServiceStub;
import example_grpc.HelloServiceOuterClass.HelloBiDirectionalStreamingRequest;
import example_grpc.HelloServiceOuterClass.HelloBiDirectionalStreamingResponse;
import example_grpc.HelloServiceOuterClass.HelloClientStreamingRequest;
import example_grpc.HelloServiceOuterClass.HelloClientStreamingResponse;
import example_grpc.HelloServiceOuterClass.HelloRequest;
import example_grpc.HelloServiceOuterClass.HelloResponse;
import example_grpc.HelloServiceOuterClass.HelloServerStreamingRequest;
import example_grpc.HelloServiceOuterClass.HelloServerStreamingResponse;
import io.grpc.ManagedChannel;
import io.grpc.netty.shaded.io.grpc.netty.GrpcSslContexts;
import io.grpc.netty.shaded.io.grpc.netty.NettyChannelBuilder;
import io.grpc.stub.StreamObserver;

// https://github.com/grpc/grpc-java/blob/master/examples/src/main/java/io/grpc/examples/routeguide/RouteGuideClient.java
public class HelloClient {

public static void main(String[] args) throws Exception {
System.out.println("Starting Client ...");

// ManagedChannel channel = ManagedChannelBuilder //
// .forAddress("localhost", 50051) //
// .usePlaintext() //
// .build();

// https://grpc.io/docs/guides/auth/#java
InputStream truststore = HelloClient.class.getClassLoader().getResourceAsStream("localhost.crt.pem");
ManagedChannel channel = NettyChannelBuilder //
.forAddress("localhost", 50051) //
.sslContext(GrpcSslContexts.forClient().trustManager(truststore).build()) //
.build();

new HelloClient().unary(channel);
// new HelloClient().serverStreaming(channel);
// new HelloClient().clientStreaming(channel);
// new HelloClient().biDirectionalStreaming(channel);

System.out.println("Shutting down Client");
channel.shutdown();
}

private void unary(ManagedChannel channel) {
HelloServiceBlockingStub syncClient = HelloServiceGrpc.newBlockingStub(channel);
// HelloServiceStub asyncClient = HelloServiceGrpc.newStub(channel);

HelloRequest req1 = HelloRequest.newBuilder().setName("Magnus").build();
HelloResponse resp1 = syncClient.hello(req1);
System.out.println("[Async] " + resp1);

HelloRequest req2 = HelloRequest.newBuilder().setName("John Doe").build();
HelloResponse resp2 = syncClient.hello(req2);
System.out.println("[Async] " + resp2);
}

}

How to Install Visual Studio Code on Fedora 33

$
0
0

Prerequisite

>$ cat /etc/fedora-release 
Fedora release 33 (Thirty Three)

Installation

https://computingforgeeks.com/install-visual-studio-code-on-fedora/

In short.

>$ sudo rpm --import https://packages.microsoft.com/keys/microsoft.asc

$ cat <<EOF | sudo tee /etc/yum.repos.d/vscode.repo
[code]
name=Visual Studio Code
baseurl=https://packages.microsoft.com/yumrepos/vscode
enabled=1
gpgcheck=1
gpgkey=https://packages.microsoft.com/keys/microsoft.asc
EOF

$ sudo dnf check-update; sudo dnf install code

$ code

How to Install Node.js on Fedora 33

$
0
0

Prerequisite

>$ cat /etc/fedora-release 
Fedora release 33 (Thirty Three)

Installation

>$ sudo dnf install nodejs 

$ node -v
v14.15.1

$ npm -v
6.14.8

Check official Node.js LTS version at https://nodejs.org/.

Optional Demo/Test

Write a small Express program and test Noe.js installation.

>$ vi http_demo_server.js
var http = require('http');

http.createServer(function (req, res) {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Welcome Node.js');
}).listen(3001, "127.0.0.1");

console.log('Server running at http://127.0.0.1:3001/');

$ node --inspect http_demo_server.js

And from another terminal test.

>$ curl http://127.0.0.1:3001/
Welcome Node.js

Getting Started with NPM and Simple Express Web App

$
0
0

Prerequisite

Install Node.js and NPM. See https://magnus-k-karlsson.blogspot.com/2021/03/how-to-install-nodejs-on-fedora-33.html.

Create new Project with NPM

>$ mkdir chapter1; cd chapter1

$ npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.

See `npm help init` for definitive documentation on these fields
and exactly what they do.

Use `npm install <pkg>` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
package name: (chapter1)
version: (1.0.0)
description: Simple Express Web App
git repository:
author: Magnus K Karlsson
license: (ISC)
About to write to /home/magnuskkarlsson/WebstormProjects/Nodde.js_from_Ground_Up_for_Beginners/chapter1/package.json:

{
"name": "chapter1",
"version": "1.0.0",
"description": "Simple Express Web App",
"main": "server.js",
"scripts": {
"test": "echo \"Error: no test specified\"&& exit 1"
},
"keywords": [],
"author": "Magnus K Karlsson",
"license": "ISC"
}


Is this OK? (yes) yes

Add Express NPM package

All NPM package can be search at https://www.npmjs.com/ and Express can be found at https://www.npmjs.com/package/express.

>$ npm install express

$ cat package.json
{
"name": "chapter1",
"version": "1.0.0",
"description": "Simple Express Web App",
"main": "server.js",
"scripts": {
"test": "echo \"Error: no test specified\"&& exit 1"
},
"keywords": [],
"author": "Magnus K Karlsson",
"license": "ISC",
"dependencies": {
"express": "^4.17.1"
}
}

Now lets write a simple Express web app.

>$ vi server.js
const express = require('express')
const app = express()

app.get('/', function (req, res) {
res.send('Hello World')
})

app.listen(3000)

And to run and test it.

>$ node server.js

$ curl localhost:3000
Hello World

Getting Started with Primefaces and Primeflex

$
0
0

Introduction

Primeflex https://www.primefaces.org/showcase/primeflex/setup.xhtml is the prefered way to do layout in Primefaces v10 https://www.primefaces.org/showcase/getstarted.xhtml

Java EE 8, Microprofile 3 and Primeface 10 Setup

pom.xml

><?xml version="1.0" encoding="UTF-8"?>
<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>se.magnuskkarlsson</groupId>
<artifactId>example-primefaces</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>war</packaging>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.build.outputEncoding>UTF-8</project.build.outputEncoding>
<failOnMissingWebXml>false</failOnMissingWebXml>
<bouncycastle.version>1.65</bouncycastle.version>
<hibernate.version>5.3.14.Final</hibernate.version>
<hibernate-validator.version>6.0.18.Final</hibernate-validator.version>
<resteasy.version>3.6.1.SP2</resteasy.version>
</properties>

<dependencies>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>8.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.eclipse.microprofile</groupId>
<artifactId>microprofile</artifactId>
<version>3.0</version>
<type>pom</type>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>org.primefaces</groupId>
<artifactId>primefaces</artifactId>
<version>10.0.0</version>
</dependency>
<dependency>
<groupId>org.primefaces.extensions</groupId>
<artifactId>primefaces-extensions</artifactId>
<version>10.0.0</version>
</dependency>

<!-- Test Support -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
<version>2.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.10.19</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.github.javafaker</groupId>
<artifactId>javafaker</artifactId>
<version>0.17.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.199</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.17</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-envers</artifactId>
<version>${hibernate.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>${hibernate-validator.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>jakarta.el</artifactId>
<version>3.0.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
<version>${resteasy.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxb-provider</artifactId>
<version>${resteasy.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-json-p-provider</artifactId>
<version>${resteasy.version}</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<finalName>${project.artifactId}</finalName>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<release>11</release>
<showDeprecation>true</showDeprecation>
<showWarnings>true</showWarnings>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>

src/main/webapp/WEB-INF/web.xml

><?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">

<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.xhtml</url-pattern>
</servlet-mapping>

<welcome-file-list>
<welcome-file>index.xhtml</welcome-file>
</welcome-file-list>
</web-app>

src/main/webapp/WEB-INF/beans.xml

><?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_2_0.xsd"
bean-discovery-mode="all" version="2.0">

</beans>

src/main/webapp/WEB-INF/faces-config.xml

><?xml version="1.0" encoding="UTF-8"?>
<faces-config xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_3.xsd"
version="2.3">

</faces-config>

Primefaces 10 Primeflex

Download Primeflex to project. You only need to minified CSS, but here we download both.

>$ mkdir -p src/main/webapp/resources/css/primeflex

$ wget https://raw.githubusercontent.com/primefaces/primeflex/master/primeflex.css -P src/main/webapp/resources/css/primeflex
$ wget https://raw.githubusercontent.com/primefaces/primeflex/master/primeflex.min.css -P src/main/webapp/resources/css/primeflex

Add Primeflex CSS as h:outputStylesheet in h:head

><h:head>
<h:outputStylesheet name="primeflex.css" library="primeflex" />
</h:head>

Test

src/main/webapp/index.html

><!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets" xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:p="http://primefaces.org/ui" xmlns:pe="http://primefaces.org/ui/extensions">

<h:head>
<h:outputStylesheet name="primeicons/primeicons.css" library="primefaces" />
<h:outputStylesheet name="primeflex.css" library="primeflex" />
<title>Hello World</title>
</h:head>
<h:body>

<div class="card">
<h:form>
<p:growl id="messages" />

<p:menubar>
<p:submenu label="File" icon="pi pi-fw pi-file">
<p:submenu label="New" icon="pi pi-fw pi-plus">
<p:menuitem value="Bookmark" icon="pi pi-fw pi-bookmark" />
<p:menuitem value="Video" icon="pi pi-fw pi-video" />
</p:submenu>
<p:menuitem value="Delete" icon="pi pi-fw pi-trash" />
<p:divider />
<p:menuitem value="Export" icon="pi pi-fw pi-external-link" />
</p:submenu>

<p:submenu label="Edit" icon="pi pi-fw pi-pencil">
<p:menuitem value="Left" icon="pi pi-fw pi-align-left" />
<p:menuitem value="Right" icon="pi pi-fw pi-align-right" />
<p:menuitem value="Center" icon="pi pi-fw pi-align-center" />
<p:menuitem value="Justify" icon="pi pi-fw pi-align-justify" />
</p:submenu>

<p:submenu label="Users" icon="pi pi-fw pi-user">
<p:menuitem value="New" icon="pi pi-fw pi-user-plus" />
<p:menuitem value="Delete" icon="pi pi-fw pi-user-minus" />
<p:submenu label="Search" icon="pi pi-fw pi-users">
<p:submenu label="Filter" icon="pi pi-fw pi-filter">
<p:menuitem value="Print" icon="pi pi-fw pi-print" />
</p:submenu>
<p:menuitem value="List" icon="pi pi-fw pi-bars" />
</p:submenu>
</p:submenu>

<p:submenu label="Events" icon="pi pi-fw pi-calendar">
<p:submenu label="Edit" icon="pi pi-fw pi-pencil">
<p:menuitem value="Save" icon="pi pi-fw pi-calendar-plus" />
<p:menuitem value="Delete" icon="pi pi-fw pi-calendar-minus" />
</p:submenu>
<p:submenu label="Archieve" icon="pi pi-fw pi-calendar-times">
<p:menuitem value="Remove" icon="pi pi-fw pi-calendar-minus" />
</p:submenu>
</p:submenu>

<p:divider layout="vertical" />

<p:menuitem value="Quit" icon="pi pi-fw pi-power-off" />

<f:facet name="options">
<div class="ui-input-icon-left p-mr-2">
<i class="pi pi-fw pi-search" />
<p:inputText placeholder="Search" />
</div>
<p:button value="Logout" icon="pi pi-fw pi-sign-out" styleClass="ui-button-info" />
</f:facet>
</p:menubar>
</h:form>
</div>

<div class="card p-mt-4">
<h:form>
<p:growl id="message" showDetail="true" />

<p:commandButton value="Confirm" action="#{confirmView.confirm}" update="message" styleClass="p-mr-2"
icon="pi pi-check">
<p:confirm header="Confirmation" message="Are you sure you want to proceed?"
icon="pi pi-exclamation-triangle" />
</p:commandButton>

<p:commandButton value="Delete" action="#{confirmView.delete}" update="message"
styleClass="ui-button-danger" icon="pi pi-times">
<p:confirm header="Confirmation" message="Do you want to delete this record?" icon="pi pi-info-circle" />
</p:commandButton>

<p:confirmDialog global="true" showEffect="fade" hideEffect="fade" responsive="true" width="350">
<p:commandButton value="No" type="button" styleClass="ui-confirmdialog-no ui-button-flat" />
<p:commandButton value="Yes" type="button" styleClass="ui-confirmdialog-yes" />
</p:confirmDialog>
</h:form>
</div>

</h:body>
</html>

jetty-maven-plugin, Java 11, JSF 2.3, Primefaces 10 and Java EE 8 with CDI 2.0

$
0
0

Introduction

Using maven jetty plugin is a fast way to develop your web application.

You run your webapp with

>$ mvn clean package jetty:run

And whenever you change something the jetty server is automatically reloaded.

pom.xml

><?xml version="1.0" encoding="UTF-8"?>
<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>se.magnuskkarlsson</groupId>
<artifactId>primefaces-jetty</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>war</packaging>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.build.outputEncoding>UTF-8</project.build.outputEncoding>
<failOnMissingWebXml>false</failOnMissingWebXml>
<bouncycastle.version>1.65</bouncycastle.version>
<hibernate.version>5.3.14.Final</hibernate.version>
<hibernate-validator.version>6.0.18.Final</hibernate-validator.version>
<resteasy.version>3.6.1.SP2</resteasy.version>
</properties>

<dependencies>
<!-- Java EE 8 -->
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>8.0</version>
<scope>provided</scope>
</dependency>

<!-- JBoss EAP 7.3 -->
<dependency>
<groupId>org.eclipse.microprofile</groupId>
<artifactId>microprofile</artifactId>
<version>3.0</version>
<type>pom</type>
<scope>provided</scope>
</dependency>

<!-- PrimeFaces -->
<dependency>
<groupId>org.primefaces</groupId>
<artifactId>primefaces</artifactId>
<version>10.0.0</version>
</dependency>
<!-- https://www.primefaces.org/showcase-ext/views/home.jsf -->
<dependency>
<groupId>org.primefaces.extensions</groupId>
<artifactId>primefaces-extensions</artifactId>
<version>10.0.0</version>
</dependency>
<!-- https://primefaces.github.io/primefaces/10_0_0/#/core/fonticons -->
<dependency>
<groupId>org.webjars</groupId>
<artifactId>font-awesome</artifactId>
<version>5.12.0</version>
</dependency>
</dependencies>

<build>
<finalName>${project.artifactId}</finalName>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<release>11</release>
<showDeprecation>true</showDeprecation>
<showWarnings>true</showWarnings>
</configuration>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<!-- https://codenotfound.com/jsf-primefaces-hello-world-example-jetty-maven.html -->
<!-- mvn clean package jetty:run -->
<plugin>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>9.4.40.v20210413</version>
<configuration>
<!-- https://www.eclipse.org/jetty/documentation/jetty-9/index.html#jetty-maven-plugin -->
<httpConnector>
<port>9090</port>
</httpConnector>
<scanIntervalSeconds>1</scanIntervalSeconds>
<webApp>
<contextPath>/primefaces-jetty</contextPath>
</webApp>
</configuration>
<dependencies>
<!-- JSF 2.3 Impl https://javaee.github.io/javaserverfaces-spec/ -->
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.faces</artifactId>
<version>2.3.8</version>
</dependency>
<!-- CDI 2.0 Impl http://fritzthecat-blog.blogspot.com/2019/08/jsf-23-maven-project-in-eclipse.html -->
<dependency>
<groupId>org.jboss.weld.servlet</groupId>
<artifactId>weld-servlet-shaded</artifactId>
<version>3.1.2.Final</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</project>

Web App

src/main/webapp/WEB-INF/web.xml

><?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">

<servlet>
<servlet-name>faces-servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>faces-servlet</servlet-name>
<url-pattern>*.xhtml</url-pattern>
</servlet-mapping>

<welcome-file-list>
<welcome-file>helloworld.xhtml</welcome-file>
</welcome-file-list>
</web-app>

src/main/webapp/WEB-INF/faces-config.xml

><?xml version="1.0" encoding="UTF-8"?>
<faces-config xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_3.xsd"
version="2.3">

</faces-config>

src/main/webapp/WEB-INF/beans.xml

><?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_2_0.xsd"
bean-discovery-mode="all" version="2.0">

</beans>

src/main/webapp/helloworld.xhtml

><!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core" xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:p="http://primefaces.org/ui">

<h:head>
<title>PrimeFaces Hello World Example</title>
</h:head>

<h:body>

<h:form>
<p:panel header="PrimeFaces Hello World Example">
<h:panelGrid columns="2" cellpadding="4">
<h:outputText value="First Name: " />
<p:inputText value="#{helloWorld.firstName}" />

<h:outputText value="Last Name: " />
<p:inputText value="#{helloWorld.lastName}" />

<p:commandButton value="Submit" update="greeting" oncomplete="PF('greetingDialog').show()" />
</h:panelGrid>
</p:panel>

<p:dialog header="Greeting" widgetVar="greetingDialog" modal="true" resizable="false">
<h:panelGrid id="greeting" columns="1" cellpadding="4">
<h:outputText value="#{helloWorld.showGreeting()}" />
</h:panelGrid>
</p:dialog>
</h:form>

</h:body>
</html>

src/main/java/se/magnuskkarlsson/example/primefaces/HelloWorld.java

>package se.magnuskkarlsson.example.primefaces;

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

@Named
@RequestScoped
public class HelloWorld {

private String firstName = "John";
private String lastName = "Doe";

// ----------------------- Logic Methods -----------------------

public String showGreeting() {
return "Hello " + firstName + "" + lastName + "!";
}

// ----------------------- Helper Methods -----------------------

// ----------------------- Get and Set Methods -----------------------

public String getFirstName() {
return firstName;
}

public void setFirstName(String firstName) {
this.firstName = firstName;
}

public String getLastName() {
return lastName;
}

public void setLastName(String lastName) {
this.lastName = lastName;
}

}

jetty-maven-plugin 10, JSF 2.3, CDI 2.0 and Primefaces 10

$
0
0

Introduction

In this blog we are going to setup a Java EE 8 project, with PrimeFaces 10, JSF 2.3 and CDI 2.0, but we are going to setup it so with can run jetty-maven-plugin for faster development.

Using jetty-maven-plugin is a great tool for faster develop server side generated dynamic web pages. You start jetty plugin with:

>$ mvn jetty:run

And have configure jetty-maven-plugin to automatically reload with configuration 'scanIntervalSeconds'.

Which Version of Jetty/jetty-maven-plugin?

For Java EE 8 which includes Servlet 4.0, JSF 2.3, CDI 2.0, EJB 3.2, JPA 2.2, JSON-P 1.1 and JSON-B 1.1.

https://en.wikipedia.org/wiki/Jakarta_EE

And Jetty 10 supports Servlet 4.

https://www.eclipse.org/jetty/download.php

Which Version of Weld (CDI 2.0 Implementation in Java EE 8)

https://weld.cdi-spec.org/documentation/

pom.xml

Now lets put this together. 2 most important reference guide for jetty-maven-plugin and Weld are:

><?xml version="1.0" encoding="UTF-8"?>
<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>se.magnuskkarlsson</groupId>
<artifactId>primefaces-jetty-10</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>war</packaging>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.build.outputEncoding>UTF-8</project.build.outputEncoding>
<failOnMissingWebXml>false</failOnMissingWebXml>
<bouncycastle.version>1.65</bouncycastle.version>
<hibernate.version>5.3.14.Final</hibernate.version>
<hibernate-validator.version>6.0.18.Final</hibernate-validator.version>
<resteasy.version>3.6.1.SP2</resteasy.version>
</properties>

<dependencies>
<!-- Java EE 8 -->
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>8.0</version>
<scope>provided</scope>
</dependency>

<!-- PrimeFaces -->
<dependency>
<groupId>org.primefaces</groupId>
<artifactId>primefaces</artifactId>
<version>10.0.0</version>
</dependency>
<!-- https://www.primefaces.org/showcase-ext/views/home.jsf -->
<dependency>
<groupId>org.primefaces.extensions</groupId>
<artifactId>primefaces-extensions</artifactId>
<version>10.0.0</version>
</dependency>
<!-- https://primefaces.github.io/primefaces/10_0_0/#/core/fonticons -->
<dependency>
<groupId>org.webjars</groupId>
<artifactId>font-awesome</artifactId>
<version>5.12.0</version>
</dependency>
</dependencies>

<build>
<finalName>${project.artifactId}</finalName>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<release>11</release>
<showDeprecation>true</showDeprecation>
<showWarnings>true</showWarnings>
</configuration>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<!-- Which Jetty version? https://www.eclipse.org/jetty/download.php -->
<!-- https://en.wikipedia.org/wiki/Jakarta_EE -->
<!-- Which WELD version? https://weld.cdi-spec.org/documentation/ -->

<!-- Java EE 8, Servlet 4.0, JSF 2.3 and CDI 2.0 = Jetty 10 and WELD 3 -->

<!-- https://www.eclipse.org/jetty/documentation/jetty-10/programming-guide/index.html#jetty-maven-plugin -->
<!-- https://docs.jboss.org/weld/reference/3.1.7.SP1/en-US/html_single/#weld-servlet -->

<!-- mvn clean package jetty:run -->
<plugin>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>10.0.2</version>
<configuration>
<httpConnector>
<port>9090</port>
</httpConnector>
<scan>1</scan>
<jettyXml>${project.basedir}/src/test/resources/jetty-env.xml</jettyXml>
<webApp>
<contextPath>/primefaces-jetty</contextPath>
<descriptor>${project.basedir}/src/test/resources/web.xml</descriptor>
</webApp>
<useTestScope>true</useTestScope>
<scanTargetPatterns>
<scanTargetPattern>
<directory>${project.basedir}/src/main/webapp/</directory>
<includes>
<include>**/*.xhtml</include>
<include>**/*.css</include>
<include>**/*.js</include>
</includes>
</scanTargetPattern>
</scanTargetPatterns>
</configuration>
<dependencies>
<!-- JSF 2.3 Impl https://javaee.github.io/javaserverfaces-spec/ -->
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.faces</artifactId>
<version>2.3.8</version>
</dependency>
<!-- WELD 3.0 (CDI 2.0 Impl/Java EE 8) -->
<dependency>
<groupId>org.jboss.weld.servlet</groupId>
<artifactId>weld-servlet-shaded</artifactId>
<version>3.1.7.SP1</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</project>

There are alot of things going on here:

  • We start Jetty on port 9090 and localhost (default)
  • Automatically reload and scan interval 1s - scan.
  • Web context path is /primefaces-jetty.
  • We use custom test web.xml - descriptor.
  • We set test scoped dependecies and test class first in classpath - useTestScope
  • CDI Weld integration - jettyXml.

Then we also add dependency to Jetty web server, since Jetty does not include JSF or CDI support.

  • org.glassfish:javax.faces
  • org.jboss.weld.servlet:weld-servlet-shaded

Lets first look at our custom ${project.basedir}/src/test/resources/web.xml

><?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">

<servlet>
<servlet-name>faces-servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>faces-servlet</servlet-name>
<url-pattern>*.xhtml</url-pattern>
</servlet-mapping>

<welcome-file-list>
<welcome-file>helloworld.xhtml</welcome-file>
</welcome-file-list>

<!-- Caused by: java.lang.IllegalStateException: Could not find backup for factory javax.faces.context.FacesContextFactory. -->
<listener>
<listener-class>com.sun.faces.config.ConfigureListener</listener-class>
</listener>

</web-app>

The only custom in this file is JSF listener com.sun.faces.config.ConfigureListener.

Next is Jetty environment xml ${project.basedir}/src/test/resources/jetty-env.xml. Which is copy from

  • Weld 3.1.7 Jetty Configuration Guide
  • .
    ><?xml version="1.0" encoding="UTF-8"?>
    <!-- <!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN""http://www.eclipse.org/jetty/configure.dtd"> -->
    <Configure id="webAppCtx" class="org.eclipse.jetty.webapp.WebAppContext">
    <New id="BeanManager" class="org.eclipse.jetty.plus.jndi.Resource">
    <Arg>
    <Ref id="webAppCtx" />
    </Arg>
    <Arg>BeanManager</Arg>
    <Arg>
    <New class="javax.naming.Reference">
    <Arg>javax.enterprise.inject.spi.BeanManager</Arg>
    <Arg>org.jboss.weld.resources.ManagerObjectFactory</Arg>
    <Arg />
    </New>
    </Arg>
    </New>
    </Configure>

    PrimeFaces 10 JSF 2.3 Web App

    Now we need a simple web app. Lets start with JSF page - src/main/webapp/helloworld.xhtml

    ><!DOCTYPE html>
    <html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://xmlns.jcp.org/jsf/html"
    xmlns:f="http://xmlns.jcp.org/jsf/core" xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
    xmlns:p="http://primefaces.org/ui">

    <h:head>
    <title>PrimeFaces Hello World Example</title>
    </h:head>

    <h:body>

    <h:form>
    <p:panel header="PrimeFaces Hello World Example">
    <h:panelGrid columns="2" cellpadding="4">
    <h:outputText value="First Name: " />
    <p:inputText value="#{helloWorld.firstName}" />

    <h:outputText value="Last Name: " />
    <p:inputText value="#{helloWorld.lastName}" />

    <p:commandButton value="Submit" update="greeting" oncomplete="PF('greetingDialog').show()" />
    </h:panelGrid>
    </p:panel>

    <p:dialog header="Greeting" widgetVar="greetingDialog" modal="true" resizable="false">
    <h:panelGrid id="greeting" columns="1" cellpadding="4">
    <h:outputText value="#{helloWorld.showGreeting()}" />
    </h:panelGrid>
    </p:dialog>
    </h:form>

    </h:body>
    </html>

    Then PRODUCTIONsrc/main/webapp/WEB-INF/web.xml

    ><?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
    version="4.0">

    <servlet>
    <servlet-name>faces-servlet</servlet-name>
    <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
    <servlet-name>faces-servlet</servlet-name>
    <url-pattern>*.xhtml</url-pattern>
    </servlet-mapping>

    <welcome-file-list>
    <welcome-file>helloworld.xhtml</welcome-file>
    </welcome-file-list>
    </web-app>

    Then PRODUCTIONsrc/main/webapp/WEB-INF/faces-config.xml

    ><?xml version="1.0" encoding="UTF-8"?>
    <faces-config xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_3.xsd"
    version="2.3">

    </faces-config>

    And next PRODUCTION beans.xml, but we do not put it in WEB-INF/, because we want to override later, so we put it in src/main/resources/META-INF/beans.xml

    ><?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_2_0.xsd"
    bean-discovery-mode="all" version="2.0">

    </beans>

    And finally our backing bean for JSF page.

    >package se.magnuskkarlsson.example.primefaces.boundary;

    import javax.enterprise.context.RequestScoped;
    import javax.inject.Inject;
    import javax.inject.Named;

    import se.magnuskkarlsson.example.primefaces.control.HelloControl;

    @Named
    @RequestScoped
    public class HelloWorld {

    private String firstName = "John";
    private String lastName = "Doe";

    @Inject
    protected HelloControl helloControl;

    // ----------------------- Logic Methods -----------------------

    public String showGreeting() {
    return helloControl.showGreeting(firstName, lastName);
    }

    // ----------------------- Helper Methods -----------------------

    // ----------------------- Get and Set Methods -----------------------

    public String getFirstName() {
    return firstName;
    }

    public void setFirstName(String firstName) {
    this.firstName = firstName;
    }

    public String getLastName() {
    return lastName;
    }

    public void setLastName(String lastName) {
    this.lastName = lastName;
    }

    }

    We introduce a Interface for our logic since we later wants to use a Mock for developing our JSF pages.

    >package se.magnuskkarlsson.example.primefaces.control;

    public interface HelloControl {

    public String showGreeting(String firstName, String lastName);

    }

    Our PRODUCTION logic implementation.

    >package se.magnuskkarlsson.example.primefaces.control;

    public class HelloControlImpl implements HelloControl {

    // ----------------------- Logic Methods -----------------------

    @Override
    public String showGreeting(String firstName, String lastName) {
    return "PROD hello " + firstName + "" + lastName + "!";
    }

    // ----------------------- Helper Methods -----------------------

    // ----------------------- Get and Set Methods -----------------------

    }

    Using standard CDI technique for using a Mock class in Jetty and Weld was hard. Tried both @Alternative and @Specializes, but neither worked. But what worked was to override custom beans.xml and it that exclude production logic class.

    src/test/java/se/magnuskkarlsson/example/primefaces/control/HelloControlMock.java

    >package se.magnuskkarlsson.example.primefaces.control;

    public class HelloControlMock implements HelloControl {

    @Override
    public String showGreeting(String firstName, String lastName) {
    return "MOCK hello " + firstName + "" + lastName + "!";
    }

    }

    And finally our custom beans.xml for development - src/test/resources/META-INF/beans.xml

    ><?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_2_0.xsd"
    bean-discovery-mode="all" version="2.0">

    <scan>
    <exclude name="se.magnuskkarlsson.example.primefaces.control.HelloControlImpl" />
    </scan>
    </beans>

    All files in total:

    >├── pom.xml
    ├── src
    │   ├── main
    │   │   ├── java
    │   │   │   └── se
    │   │   │   └── magnuskkarlsson
    │   │   │   └── example
    │   │   │   └── primefaces
    │   │   │   ├── boundary
    │   │   │   │   └── HelloWorld.java
    │   │   │   └── control
    │   │   │   ├── HelloControlImpl.java
    │   │   │   └── HelloControl.java
    │   │   ├── resources
    │   │   │   └── META-INF
    │   │   │   └── beans.xml
    │   │   └── webapp
    │   │   ├── helloworld.xhtml
    │   │   └── WEB-INF
    │   │   ├── faces-config.xml
    │   │   └── web.xml
    │   └── test
    │   ├── java
    │   │   └── se
    │   │   └── magnuskkarlsson
    │   │   └── example
    │   │   └── primefaces
    │   │   └── control
    │   │   └── HelloControlMock.java
    │   └── resources
    │   ├── jetty-env.xml
    │   ├── META-INF
    │   │   └── beans.xml
    │   └── web.xml

    Test

    Lets start web app with jetty

    >$ mvn clean package jetty:run
    ...
    [INFO] --- jetty-maven-plugin:10.0.2:run (default-cli) @ primefaces-jetty-10 ---
    [INFO] Configuring Jetty for project: primefaces-jetty-10
    [INFO] Classes = /home/magnuskkarlsson/eclipse-workspace/primefaces-jetty-10/target/classes
    [INFO] Context path = /primefaces-jetty
    [INFO] Tmp directory = /home/magnuskkarlsson/eclipse-workspace/primefaces-jetty-10/target/tmp
    [INFO] web.xml file = /home/magnuskkarlsson/eclipse-workspace/primefaces-jetty-10/src/test/resources/web.xml
    [INFO] Webapp directory = /home/magnuskkarlsson/eclipse-workspace/primefaces-jetty-10/src/main/webapp
    [INFO] Web defaults = org/eclipse/jetty/webapp/webdefault.xml
    [INFO] Web overrides = none
    [INFO] jetty-10.0.2; built: 2021-03-26T06:15:43.282Z; git: 7bd207b30931f3f61d110b1121118fbb5d10cb48; jvm 11.0.10+9
    May 17, 2021 4:30:22 PM org.jboss.weld.environment.servlet.EnhancedListener onStartup
    INFO: WELD-ENV-001008: Initialize Weld using ServletContainerInitializer
    May 17, 2021 4:30:22 PM org.jboss.weld.bootstrap.WeldStartup <clinit>
    INFO: WELD-000900: 3.1.7 (SP1)
    May 17, 2021 4:30:22 PM org.jboss.weld.bootstrap.WeldStartup startContainer
    INFO: WELD-000101: Transactional services not available. Injection of @Inject UserTransaction not available. Transactional observers will be invoked synchronously.
    May 17, 2021 4:30:22 PM org.jboss.weld.environment.jetty.JettyLegacyContainer initialize
    INFO: WELD-ENV-001200: Jetty 7.2+ detected, CDI injection will be available in Servlets and Filters. Injection into Listeners should work on Jetty 9.1.1 and newer.
    [INFO] Session workerName=node0
    May 17, 2021 4:30:23 PM com.sun.faces.config.ConfigureListener contextInitialized
    INFO: Initializing Mojarra 2.3.8 ( 20181116-0037 55af8b79ca53ec2df566f9c08a430259d30f9ba5) for context '/primefaces-jetty'
    May 17, 2021 4:30:23 PM com.sun.faces.spi.InjectionProviderFactory createInstance
    INFO: JSF1048: PostConstruct/PreDestroy annotations present. ManagedBeans methods marked with these annotations will have said annotations processed.
    May 17, 2021 4:30:24 PM org.primefaces.webapp.PostConstructApplicationEventListener processEvent
    INFO: Running on PrimeFaces 10.0.0
    May 17, 2021 4:30:24 PM org.primefaces.extensions.application.PostConstructApplicationEventListener processEvent
    INFO: Running on PrimeFaces Extensions 10.0.0
    [INFO] Started o.e.j.m.p.MavenWebAppContext@7de147e9{/primefaces-jetty,[file:///home/magnuskkarlsson/eclipse-workspace/primefaces-jetty-10/src/main/webapp/, jar:file:///home/magnuskkarlsson/.m2/repository/org/primefaces/extensions/primefaces-extensions/10.0.0/primefaces-extensions-10.0.0.jar!/META-INF/resources, jar:file:///home/magnuskkarlsson/.m2/repository/org/primefaces/primefaces/10.0.0/primefaces-10.0.0.jar!/META-INF/resources, jar:file:///home/magnuskkarlsson/.m2/repository/org/webjars/font-awesome/5.12.0/font-awesome-5.12.0.jar!/META-INF/resources],AVAILABLE}{file:///home/magnuskkarlsson/eclipse-workspace/primefaces-jetty-10/src/main/webapp/}
    [INFO] Started ServerConnector@30b29f55{HTTP/1.1, (http/1.1)}{0.0.0.0:9090}
    [INFO] Started Server@2094bf3d{STARTING}[10.0.2,sto=0] @6979ms
    [INFO] Scan interval sec = 1

    Now deploy to local JBos EAP 7.3

    >$ rm -rf ~/bin/jboss-eap-7.3.0/standalone/deployments/primefaces-jetty*; mvn clean install; cp -f target/primefaces-jetty-10.war ~/bin/jboss-eap-7.3.0/standalone/deployments
    Viewing all 526 articles
    Browse latest View live