Java

From campisano.org
Jump to navigation Jump to search

Java

See Maven build automation tool to setup a java project.

Options

Examples:

note, use

java -XX:+PrintCommandLineFlags -version

to veryfy what you got, for example

-XX:ConcGCThreads=2 -XX:G1ConcRefinementThreads=6 -XX:GCDrainStackTargetSize=64 -XX:InitialHeapSize=522556352 -XX:MarkStackSize=4194304 -XX:MaxHeapSize=8360901632 -XX:MinHeapSize=6815736 -XX:+PrintCommandLineFlags -XX:ReservedCodeCacheSize=251658240 -XX:+SegmentedCodeCache -XX:+UseCompressedOops -XX:+UseG1GC 
openjdk version "21" 2023-09-19
OpenJDK Runtime Environment (build 21+35-2513)
OpenJDK 64-Bit Server VM (build 21+35-2513, mixed mode, sharing)

or also

java -XX:+UnlockDiagnosticVMOptions -XX:+PrintFlagsFinal -version
  • an example
-XX:+UseContainerSupport -XX:-UseStringDeduplication -XX:+CompactStrings -XX:-DisableExplicitGC -XX:ActiveProcessorCount=-1 -XX:MinHeapFreeRatio=40 -XX:MaxHeapFreeRatio=70 -XX:GCTimeRatio=12
  • small memory
 -XX:ActiveProcessorCount=2 -XX:+UseStringDeduplication -XX:MinHeapFreeRatio=20 -XX:MaxHeapFreeRatio=35 -XX:GCTimeRatio=24 -Dlog4j.formatMsgNoLookups=true -XX:+UseSerialGC
  • small pauses
-Xms16g -Xmx16g -XX:-UseContainerSupport -XX:+AlwaysPreTouch -XX:MaxGCPauseMillis=50 -Dlog4j.formatMsgNoLookups=true -XX:+UseG1GC

or, if your JVM supports ShenandoahGC (see https://stackoverflow.com/a/69231190)

-Xms16g -Xmx16g -XX:-UseContainerSupport -XX:+AlwaysPreTouch -XX:MaxGCPauseMillis=50 -Dlog4j.formatMsgNoLookups=true -XX:+UseShenandoahGC

GC tuning

from https://docs.oracle.com/en/java/javase/17/gctuning/ergonomics.html#GUID-BC516CBE-700D-44DB-8485-3FD5CA9A411B

The Java HotSpot VM garbage collectors can be configured to preferentially meet one of two goals: maximum pause-time and application throughput. If the preferred goal is met, the collectors will try to maximize the other.

The pause time is the duration during which the garbage collector stops the application and recovers space that's no longer in use. The intent of the maximum pause-time goal is to limit the longest of these pauses. If the average plus the variance of the pause-time is greater than the maximum pause-time goal, then the garbage collector considers that the goal isn't being met. The maximum pause-time goal is specified with the command-line option -XX:MaxGCPauseMillis=<nnn>. This is interpreted as a hint to the garbage collector that a pause-time of <nnn> milliseconds or fewer is desired. The garbage collector adjusts the Java heap size and other parameters related to garbage collection in an attempt to keep garbage collection pauses shorter than <nnn> milliseconds.

The throughput goal is measured in terms of the time spent collecting garbage, and the time spent outside of garbage collection is the application time. The goal is specified by the command-line option -XX:GCTimeRatio=nnn. The ratio of garbage collection time to application time is 1/ (1+nnn). For example, -XX:GCTimeRatio=19 sets a goal of 1/20th or 5% of the total time for garbage collection. If the throughput goal isn't being met, then one possible action for the garbage collector is to increase the size of the heap so that the time spent in the application between collection pauses can be longer.

If the throughput and maximum pause-time goals have been met, then the garbage collector reduces the size of the heap until one of the goals (invariably the throughput goal) can't be met. The minimum and maximum heap sizes that the garbage collector can use can be set using -Xms=<nnn> and -Xmx=<mmm> for minimum and maximum heap size respectively.

The heap grows or shrinks to a size that supports the chosen throughput goal. Learn about heap tuning strategies such as choosing a maximum heap size, and choosing maximum pause-time goal. Don't choose a maximum value for the heap unless you know that you need a heap greater than the default maximum heap size. Choose a throughput goal that's sufficient for your application.

from https://docs.oracle.com/en/graalvm/jdk/17/docs/reference-manual/native-image/optimizations-and-performance/MemoryManagement/#overview-1

If no maximum Java heap size is specified, a native image that uses the G1 GC will set its maximum Java heap size to 25% of the physical memory size. For example, on a machine with 4GB of RAM, the maximum Java heap size will be set to 1GB. If the same image is executed on a machine that has 32GB of RAM, the maximum Java heap size will be set to 8GB. To override this default behavior, either specify a value for -XX:MaxRAMPercentage or explicitly set the maximum Java heap size.

from https://docs.oracle.com/en/java/javase/17/gctuning/factors-affecting-garbage-collection-performance.html#GUID-7FB2D1D5-D75F-4AA1-A3B1-4A17F8FF97D0

Minimize Java heap size by lowering the values of the options -XX:MaxHeapFreeRatio (default value is 70%) and -XX:MinHeapFreeRatio (default value is 40%) with the command-line options -XX:MaxHeapFreeRatio and -XX:MinHeapFreeRatio. Lowering -XX:MaxHeapFreeRatio to as low as 10% and -XX:MinHeapFreeRatio has shown to successfully reduce the heap size without too much performance degradation...

About using percentage, be aware of https://blog.arkey.fr/2020/10/27/maxrampercentage-is-not-what-i-wished-for/ Note (from the previous link), for any kind of java defaults, use the great Chris Newland tool https://chriswhocodes.com/hotspot_options_openjdk17.html

Develop web apps

Example SpringBoot app

  • Use Maven to generate the project structure
mvn archetype:generate -DarchetypeGroupId=org.apache.maven.archetypes -DarchetypeArtifactId=maven-archetype-quickstart -DgroupId=org.example -DartifactId=test_java_springbooot_scratch -Dversion=0.1.0 -DinteractiveMode=false
cd test_java_springbooot_scratch
  • Configure pom.xml

Remove uninteresting stuff from maven pom.xml file

sed -i '/<properties>/q' pom.xml

Edit the pom.xml and add the follow configs

cat >> pom.xml << 'EOF'
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <java.version>17</java.version>
    <maven.compiler.source>${java.version}</maven.compiler.source>
    <maven.compiler.target>${java.version}</maven.compiler.target>
  </properties>
EOF

Add spring boot dependencies and plugins (from https://start.spring.io/)

cat >> pom.xml << 'EOF'

  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.6.4</version>
    <relativePath/>
  </parent>

  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
    </plugins>
  </build>

</project>
EOF
  • Setup a SpringBoot app
cat >> src/main/java/org/example/App.java << 'EOF'
package org.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class App {

    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }
}
EOF
cat >> src/test/java/org/example/AppTest.java << 'EOF'
package org.example;

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class AppTest {

    @Test
    public void contextLoads() {
    }
}
EOF
mkdir -p src/main/java/org/example/adapters/controllers/
cat >> src/main/java/org/example/adapters/controllers/Greeting.java << 'EOF'
package org.example.adapters.controllers;

public class Greeting {

    private final long id;
    private final String content;

    public Greeting(long id, String content) {
        this.id = id;
        this.content = content;
    }

    public long getId() {
        return id;
    }

    public String getContent() {
        return content;
    }
}
EOF
cat >> src/main/java/org/example/adapters/controllers/GreetingController.java << 'EOF'
package org.example.adapters.controllers;

import java.util.concurrent.atomic.AtomicLong;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class GreetingController {

    private static final String template = "Hello, %s!";
    private final AtomicLong counter = new AtomicLong();

    @GetMapping("/greeting")
    public Greeting greeting(@RequestParam(value = "name", defaultValue = "World") String name) {
        return new Greeting(counter.incrementAndGet(), String.format(template, name));
    }
}
EOF
  • Check
mvn clean install spring-boot:run
# and from another shell
curl localhost:8080/greeting?name=test


WSDL/SOAP

Get a sample WSDL definition

A good start point for a WSDL definition "SHOULD" be the one in https://www.w3.org/TR/wsdl.html#_wsdl , however the tools used below had problems with it, you should:

  • Substitute the xmlns schema definition from
xmlns="http://www.w3.org/2000/10/XMLSchema"

To

xmlns="http://www.w3.org/2001/XMLSchema"
  • And the binding name from
binding name="StockQuoteSoapBinding"

To

binding name="StockQuoteBinding"

To obtain the final service.wsdl content you can run the follow command:

cat >> service.wsdl << 'EOF'
<?xml version="1.0"?>
<definitions name="StockQuote"

targetNamespace="http://example.com/stockquote.wsdl"
          xmlns:tns="http://example.com/stockquote.wsdl"
          xmlns:xsd1="http://example.com/stockquote.xsd"
          xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
          xmlns="http://schemas.xmlsoap.org/wsdl/">

    <types>
       <schema targetNamespace="http://example.com/stockquote.xsd"
              xmlns="http://www.w3.org/2001/XMLSchema">
           <element name="TradePriceRequest">
              <complexType>
                  <all>
                      <element name="tickerSymbol" type="string"/>
                  </all>
              </complexType>
           </element>
           <element name="TradePrice">
              <complexType>
                  <all>
                      <element name="price" type="float"/>
                  </all>
              </complexType>
           </element>
       </schema>
    </types>

    <message name="GetLastTradePriceInput">
        <part name="body" element="xsd1:TradePriceRequest"/>
    </message>

    <message name="GetLastTradePriceOutput">
        <part name="body" element="xsd1:TradePrice"/>
    </message>

    <portType name="StockQuotePortType">
        <operation name="GetLastTradePrice">
           <input message="tns:GetLastTradePriceInput"/>
           <output message="tns:GetLastTradePriceOutput"/>
        </operation>
    </portType>

    <binding name="StockQuoteBinding" type="tns:StockQuotePortType">
        <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
        <operation name="GetLastTradePrice">
           <soap:operation soapAction="http://example.com/GetLastTradePrice"/>
           <input>
               <soap:body use="literal"/>
           </input>
           <output>
               <soap:body use="literal"/>
           </output>
        </operation>
    </binding>

    <service name="StockQuoteService">
        <documentation>My first service</documentation>
        <port name="StockQuotePort" binding="tns:StockQuoteBinding">
           <soap:address location="http://example.com/stockquote"/>
        </port>
    </service>

</definitions>
EOF

Others useful WSDL definitions that can be tested querying online servers in internet are:

So get the WSDL definition you prefer and save it in a service.wsdl file, we will use it.

Generate Java code from WSDL definition

Using Sun jaxws-maven-plugin
  • Use Maven to generate the project structure
mvn archetype:generate -DarchetypeGroupId=org.apache.maven.archetypes -DarchetypeArtifactId=maven-archetype-quickstart -DgroupId=org.example -DartifactId=test_java_wsdl_import_jaxws -Dversion=0.1.0 -DinteractiveMode=false
cd test_java_wsdl_import_jaxws
  • Configure pom.xml

Remove uninteresting stuff from maven pom.xml file

sed -i '/<properties>/q' pom.xml

Edit the pom.xml and add the follow configs

cat >> pom.xml << 'EOF'
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</maven.compiler.target>
    <maven-compiler-plugin.version>3.8.1</maven-compiler-plugin.version>
    <jaxws-maven-plugin.version>4.0.0-M4</jaxws-maven-plugin.version>
    <junit.version>4.11</junit.version>
  </properties>
EOF

Add the follow dependencies and plugins (from https://eclipse-ee4j.github.io/metro-jax-ws/jaxws-maven-plugin/examples/using-wsdlLocation.html , https://eclipse-ee4j.github.io/metro-jax-ws/jaxws-maven-plugin/ , https://www.baeldung.com/java-soap-web-service#jdk11maven-wsimport)

cat >> pom.xml << 'EOF'

  <dependencies>
    <dependency>
      <groupId>com.sun.xml.ws</groupId>
      <artifactId>jaxws-maven-plugin</artifactId>
      <version>${jaxws-maven-plugin.version}</version>
    </dependency>

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>${junit.version}</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <build>
    <plugins>
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>${maven-compiler-plugin.version}</version>
          <configuration>
            <source>${maven.compiler.source.version}</source>
            <target>${maven.compiler.target.version}</target>
          </configuration>
        </plugin>
        <!-- jax-ws maven plugin -->
        <plugin>
          <groupId>com.sun.xml.ws</groupId>
          <artifactId>jaxws-maven-plugin</artifactId>
          <version>${jaxws-maven-plugin.version}</version>
          <configuration>
            <wsdlFiles>
              <wsdlFile>${basedir}/src/main/resources/service.wsdl</wsdlFile>
            </wsdlFiles>
            <keep>true</keep>
            <packageName>org.example.adapter.soap.generated</packageName>
            <sourceDestDir>src/main/java</sourceDestDir>
          </configuration>
        </plugin>
    </plugins>
  </build>
</project>
EOF

Put the previously obtained service.wsdl to the folder src/main/resources/ , for instance

mkdir -p src/main/resources/
cp ../service.wsdl src/main/resources/
  • Finally generates Java classes from wsdl, to do that run
mvn clean jaxws:wsimport
Using Apache CXF maven plugin
  • Use Maven to generate the project structure
mvn archetype:generate -DarchetypeGroupId=org.apache.maven.archetypes -DarchetypeArtifactId=maven-archetype-quickstart -DgroupId=org.example -DartifactId=test_java_wsdl_import_cxf -Dversion=0.1.0 -DinteractiveMode=false
cd test_java_wsdl_import_cxf
  • Configure pom.xml

Remove uninteresting stuff from maven pom.xml file

sed -i '/<properties>/q' pom.xml

Edit the pom.xml and add the follow configs

cat >> pom.xml << 'EOF'
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</maven.compiler.target>
    <maven-compiler-plugin.version>3.8.1</maven-compiler-plugin.version>
    <cxf-rt.version>3.5.2</cxf-rt.version>
    <junit.version>4.11</junit.version>
  </properties>
EOF

Add the follow dependencies and plugins (from https://cxf.apache.org/docs/maven-cxf-codegen-plugin-wsdl-to-java.html)

cat >> pom.xml << 'EOF'

  <dependencies>
    <dependency>
      <groupId>org.apache.cxf</groupId>
      <artifactId>cxf-rt-wsdl</artifactId>
      <version>${cxf-rt.version}</version>
    </dependency>

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>${junit.version}</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>${maven-compiler-plugin.version}</version>
        <configuration>
          <source>${maven.compiler.source.version}</source>
          <target>${maven.compiler.target.version}</target>
        </configuration>
      </plugin>

      <plugin>
        <groupId>org.apache.cxf</groupId>
        <artifactId>cxf-codegen-plugin</artifactId>
        <version>${cxf-rt.version}</version>
        <executions>
          <execution>
            <id>generate-sources</id>
            <phase>generate-sources</phase>
            <configuration>
              <sourceRoot>${basedir}/src/main/java</sourceRoot>
              <wsdlOptions>
                <wsdlOption>
                  <wsdl>${basedir}/src/main/resources/service.wsdl</wsdl>
                  <extraargs>
                    <extraarg>-verbose</extraarg>
                    <extraarg>-all</extraarg>
                    <extraarg>-p</extraarg>
                    <extraarg>org.example.adapter.soap.generated</extraarg>
                  </extraargs>
                </wsdlOption>
              </wsdlOptions>
            </configuration>
            <goals>
              <goal>wsdl2java</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
</project>
EOF

Put the previously obtained service.wsdl to the folder src/main/resources/ , for instance

mkdir -p src/main/resources/
cp ../service.wsdl src/main/resources/
  • Finally generates Java classes from wsdl, to do that run
mvn clean generate-sources

This was sufficient to generate client and server classes from wsdl definition, however to see the server class in action, we need to add the follow dependencies to the pom.xml file:

    <dependency>
      <groupId>org.apache.cxf</groupId>
      <artifactId>cxf-rt-frontend-jaxws</artifactId>
      <version>${cxf-rt.version}</version>
    </dependency>

    <dependency>
      <groupId>org.apache.cxf</groupId>
      <artifactId>cxf-rt-transports-http-jetty</artifactId>
      <version>${cxf-rt.version}</version>
    </dependency>

And to change the address string in the StockQuotePortType_StockQuotePort_Server class from

String address = "http://example.com/stockquote";

To

String address = "http://localhost:8080/stockquote";
Using org.codehaus.mojo jaxws-maven-plugin
  • Use Maven to generate the project structure
mvn archetype:generate -DarchetypeGroupId=org.apache.maven.archetypes -DarchetypeArtifactId=maven-archetype-quickstart -DgroupId=org.example -DartifactId=test_java_wsdl_import_codehaus -Dversion=0.1.0 -DinteractiveMode=false
cd test_java_wsdl_import_codehaus
  • Configure pom.xml

Remove uninteresting stuff from maven pom.xml file

sed -i '/<properties>/q' pom.xml

Edit the pom.xml and add the follow configs

cat >> pom.xml << 'EOF'
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</maven.compiler.target>
    <maven-compiler-plugin.version>3.8.1</maven-compiler-plugin.version>
    <codehaus-mojo-jaxws-maven-plugin.version>2.6</codehaus-mojo-jaxws-maven-plugin.version>
    <junit.version>4.11</junit.version>
  </properties>
EOF

Add the follow dependencies and plugins (https://github.com/mojohaus/jaxws-maven-plugin)

cat >> pom.xml << 'EOF'

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>${junit.version}</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>${maven-compiler-plugin.version}</version>
        <configuration>
          <source>${maven.compiler.source.version}</source>
          <target>${maven.compiler.target.version}</target>
        </configuration>
      </plugin>

      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>jaxws-maven-plugin</artifactId>
        <version>${codehaus-mojo-jaxws-maven-plugin.version}</version>
        <executions>
          <execution>
            <goals>
              <goal>wsimport</goal>
            </goals>
            <configuration>
              <wsdlFiles>
                <wsdlFile>${project.basedir}/src/main/resources/service.wsdl</wsdlFile>
              </wsdlFiles>
              <wsdlLocation>https://YOUR_WSDL_URL?WSDL</wsdlLocation>
              <keep>true</keep>
              <genJWS>false</genJWS>
              <packageName>org.example.adapter.soap.generated</packageName>
              <sourceDestDir>src/main/java</sourceDestDir>
            </configuration>
          </execution>
        </executions>
      </plugin>

    </plugins>
  </build>
</project>
EOF

Put the previously obtained service.wsdl to the folder src/main/resources/ , for instance

mkdir -p src/main/resources/
cp ../service.wsdl src/main/resources/
  • Finally generates Java classes from wsdl, to do that run
mvn clean generate-sources
Using org.jvnet.jaxb2.maven2 maven plugin
  • Use Maven to generate the project structure
mvn archetype:generate -DarchetypeGroupId=org.apache.maven.archetypes -DarchetypeArtifactId=maven-archetype-quickstart -DgroupId=org.example -DartifactId=test_java_wsdl_import_jvnet -Dversion=0.1.0 -DinteractiveMode=false
cd test_java_wsdl_import_jvnet
  • Configure pom.xml

Remove uninteresting stuff from maven pom.xml file

sed -i '/<properties>/q' pom.xml

Edit the pom.xml and add the follow configs

cat >> pom.xml << 'EOF'
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</maven.compiler.target>
    <maven-compiler-plugin.version>3.8.1</maven-compiler-plugin.version>
    <jvnet-maven-jaxb2-plugin.version>0.15.1</jvnet-maven-jaxb2-plugin.version>
    <junit.version>4.11</junit.version>
  </properties>
EOF

Add the follow dependencies and plugins (from https://github.com/highsource/maven-jaxb2-plugin)

cat >> pom.xml << 'EOF'

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>${junit.version}</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>${maven-compiler-plugin.version}</version>
        <configuration>
          <source>${maven.compiler.source.version}</source>
          <target>${maven.compiler.target.version}</target>
        </configuration>
      </plugin>

      <plugin>
        <groupId>org.jvnet.jaxb2.maven2</groupId>
        <artifactId>maven-jaxb2-plugin</artifactId>
        <version>${jvnet-maven-jaxb2-plugin.version}</version>
        <executions>
          <execution>
            <id>generate</id>
            <goals>
              <goal>generate</goal>
            </goals>
          </execution>
        </executions>
        <configuration>
          <schemaLanguage>WSDL</schemaLanguage>
          <schemaDirectory>src/main/resources</schemaDirectory>
          <schemaIncludes>
            <include>*.wsdl</include>
          </schemaIncludes>
          <args>
            <arg>-wsdl</arg>
          </args>
          <generatePackage>org.example.adapter.soap.generated</generatePackage>
          <generateDirectory>src/main/java</generateDirectory>
        </configuration>
      </plugin>

    </plugins>
  </build>
</project>
EOF

Put the previously obtained service.wsdl to the folder src/main/resources/ , for instance

mkdir -p src/main/resources/
cp ../service.wsdl src/main/resources/
  • Finally generates Java classes from wsdl, to do that run
mvn clean generate-sources

Links

Patterns

Webmail apps

About Multiple Forms