June 1, 2021

MicroProfile OpenAPI on Wildfly 23 and JBoss EAP 7.3.x

Introduction

In my last blog I showed a simple example app annotated with Eclipse MicroProfile OpenAPI Annotation. See https://magnus-k-karlsson.blogspot.com/2021/05/auto-generate-openapiswagger.html

Here will deploy it on Wildfly and call OpenAPI.

Wildfly 23.0.2.Final

On Wildfly is Eclipse MicroProfile already installed and all you need is to start it with the correct configuration.

$ ./standalone.sh -c standalone-microprofile.xml

And then call it

$ curl http://localhost:8080/openapi
---
openapi: 3.0.3
info:
  title: example-openapi-swagger.war
  version: "1.0"
servers:
- url: /example-openapi-swagger
paths:
  /api/persons:
    get:
      summary: Get all persons.
      description: Get all persons in DB.
      parameters:
      - name: name
        in: query
        description: The name to search for.
        required: false
        schema:
          type: string
        example: '*he*'
      responses:
        "500":
          description: Internal Error
          content:
            application/json: {}
        "200":
          description: All persons in DB.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Person'
components:
  schemas:
    Person:
      type: object
      properties:
        age:
          format: int32
          type: integer
        name:
          type: string

And to get JSON instead.

$ curl http://localhost:8080/openapi?format=JSON  
{
  "openapi" : "3.0.3",
  "info" : {
    "title" : "example-openapi-swagger.war",
    "version" : "1.0"
  },
  "servers" : [ {
    "url" : "/example-openapi-swagger"
  } ],
  "paths" : {
    "/api/persons" : {
      "get" : {
        "summary" : "Get all persons.",
        "description" : "Get all persons in DB.",
        "parameters" : [ {
          "name" : "name",
          "in" : "query",
          "description" : "The name to search for.",
          "required" : false,
          "schema" : {
            "type" : "string"
          },
          "example" : "*he*"
        } ],
        "responses" : {
          "500" : {
            "description" : "Internal Error",
            "content" : {
              "application/json" : { }
            }
          },
          "200" : {
            "description" : "All persons in DB.",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/Person"
                }
              }
            }
          }
        }
      }
    }
  },
  "components" : {
    "schemas" : {
      "Person" : {
        "type" : "object",
        "properties" : {
          "age" : {
            "format" : "int32",
            "type" : "integer"
          },
          "name" : {
            "type" : "string"
          }
        }
      }
    }
  }
}

JBoss EAP 7.3

The JBoss EAP does not come bundled with OpenAPI and you need to install JBOSS EAP XP 2.0.0. See https://access.redhat.com/documentation/en-us/red_hat_jboss_enterprise_application_platform/7.3/pdf/using_eclipse_microprofile_with_jboss_eap_xp_2.0.0/Red_Hat_JBoss_Enterprise_Application_Platform-7.3-Using_Eclipse_MicroProfile_with_JBoss_EAP_XP_2.0.0-en-US.pdf

May 31, 2021

Auto Generate OpenAPI/Swagger Specification from Annotated Java Code, with maven, Java 11, Eclipse Microprofile, Java EE 8

OpenAPI Specification (formerly Swagger Specification)

Auto generate documentation for your REST API, is a great way to document and it's auto generated and finally you follows a specification, that people have thought throw.

OpenAPI (part of Eclise Microprofile) Annotation

Maven dependency.

        <dependency>
            <groupId>org.eclipse.microprofile</groupId>
            <artifactId>microprofile</artifactId>
            <version>3.0</version>
            <type>pom</type>
            <scope>provided</scope>
        </dependency>

And annotated Java code.

package se.magnuskkarlsson.example_openapi_swagger.boundary;

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

import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;

import org.eclipse.microprofile.openapi.annotations.Operation;
import org.eclipse.microprofile.openapi.annotations.enums.SchemaType;
import org.eclipse.microprofile.openapi.annotations.media.Content;
import org.eclipse.microprofile.openapi.annotations.media.Schema;
import org.eclipse.microprofile.openapi.annotations.parameters.Parameter;
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
import org.eclipse.microprofile.openapi.annotations.responses.APIResponses;

import se.magnuskkarlsson.example_openapi_swagger.entity.Person;

@Path("/persons")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public class PersonResource {

    @GET
    @Operation(summary = "Get all persons.", description = "Get all persons in DB.")
    @APIResponses(value = { //
            @APIResponse( //
                    responseCode = "500", //
                    description = "Internal Error", //
                    content = @Content(mediaType = MediaType.APPLICATION_JSON)),
            @APIResponse( //
                    responseCode = "200", //
                    description = "All persons in DB.", //
                    content = @Content( //
                            mediaType = MediaType.APPLICATION_JSON, //
                            schema = @Schema(implementation = Person.class))) })
    public List<Person> search( //
            @Parameter( //
                    description = "The name to search for.", //
                    required = false, //
                    example = "*he*", //
                    schema = @Schema(type = SchemaType.STRING)) //
            @QueryParam("name") String name) {
        var persons = new ArrayList<Person>();

        var person1 = new Person().setName("FOO").setAge(24);
        persons.add(person1);

        var person2 = new Person().setName("BAR").setAge(51);
        persons.add(person2);

        return persons;
    }

}

And maven plugin, which will auto generate OpenAPI specification and output in YAML file in your webapp root folder.

            <!-- https://github.com/kongchen/swagger-maven-plugin -->
            <plugin>
                <groupId>com.github.kongchen</groupId>
                <artifactId>swagger-maven-plugin</artifactId>
                <version>3.1.8</version>
                <configuration>
                    <apiSources>
                        <apiSource>
                            <springmvc>false</springmvc>
                            <locations>se.magnuskkarlsson.example_openapi_swagger.boundary</locations>
                            <!-- <schemes>http</schemes> <host>localhost:8081</host> -->
                            <basePath>/${project.build.finalName}</basePath>
                            <info>
                                <title>Users API</title>
                                <version>v1</version>
                                <description>Users rest endpoints</description>
                            </info>
                            <outputFormats>yaml</outputFormats>
                            <swaggerDirectory>${basedir}/src/main/webapp</swaggerDirectory>
                        </apiSource>
                    </apiSources>
                </configuration>
                <executions>
                    <execution>
                        <phase>compile</phase>
                        <goals>
                            <goal>generate</goal>
                        </goals>
                    </execution>
                </executions>
                <dependencies>
                    <dependency>
                        <groupId>javax.xml.bind</groupId>
                        <artifactId>jaxb-api</artifactId>
                        <version>2.3.1</version>
                    </dependency>
                </dependencies>
            </plugin>

Reference:

Generated OpenAPI YAML Specification

---
swagger: "2.0"
info:
  description: "Users rest endpoints"
  version: "v1"
  title: "Users API"
basePath: "/example-openapi-swagger-1.0.0-SNAPSHOT"
paths:
  /persons:
    get:
      operationId: "search"
      consumes:
      - "application/json"
      produces:
      - "application/json"
      parameters:
      - name: "name"
        in: "query"
        required: false
        type: "string"
      responses:
        200:
          description: "successful operation"
          schema:
            type: "array"
            items:
              $ref: "#/definitions/Person"
definitions:
  Person:
    type: "object"
    properties:
      name:
        type: "string"
      age:
        type: "integer"
        format: "int32"

Complete 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-openapi-swagger</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>
        <primefaces.version>10.0.0</primefaces.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>

        <!-- 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>
    </dependencies>

    <build>
        <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/kongchen/swagger-maven-plugin -->
            <plugin>
                <groupId>com.github.kongchen</groupId>
                <artifactId>swagger-maven-plugin</artifactId>
                <version>3.1.8</version>
                <configuration>
                    <apiSources>
                        <apiSource>
                            <springmvc>false</springmvc>
                            <locations>se.magnuskkarlsson.example_openapi_swagger.boundary</locations>
                            <!-- <schemes>http</schemes> <host>localhost:8081</host> -->
                            <basePath>/${project.build.finalName}</basePath>
                            <info>
                                <title>Users API</title>
                                <version>v1</version>
                                <description>Users rest endpoints</description>
                            </info>
                            <outputFormats>yaml</outputFormats>
                            <swaggerDirectory>${basedir}/src/main/webapp</swaggerDirectory>
                        </apiSource>
                    </apiSources>
                </configuration>
                <executions>
                    <execution>
                        <phase>compile</phase>
                        <goals>
                            <goal>generate</goal>
                        </goals>
                    </execution>
                </executions>
                <dependencies>
                    <dependency>
                        <groupId>javax.xml.bind</groupId>
                        <artifactId>jaxb-api</artifactId>
                        <version>2.3.1</version>
                    </dependency>
                </dependencies>
            </plugin>
        </plugins>
    </build>

</project>

May 17, 2021

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

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

    Then PRODUCTION 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>
    

    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
    

    May 14, 2021

    jetty-maven-plugin, Java 11, JSF 2.3, Primefaces 10 and Java EE 8 with CDI 2.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;
        }
    
    }
    

    April 12, 2021

    Getting Started with Primefaces and Primeflex

    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>
    

    March 26, 2021

    Getting Started with NPM and Simple Express Web App

    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
    

    How to Install Node.js on Fedora 33

    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
    

    How to Install Visual Studio Code on Fedora 33

    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
    

    March 25, 2021

    gRPC and SSL/TLS Ciphers and Protocol Version

    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);
        }
        
    }
    

    Overvew gRPC (Remote Procedure Call)

    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);
        }