30 September 2019
As you probably know Java EE was moved from Oracle to the Eclipse Foundation where will evolve under the Jakarta EE brand. Sept. 10, 2019 Jakarta EE Full Platform and Web Profile specifications was released by Eclipse Foundation during JakartaOne Livestream. Few days later Wildfly declared that WildFly 17.0.1 has passed the Jakarta EE 8 TCK and certification request has been approved by the Jakarta EE Spec Committee. So, now WildFly is a Jakarta EE Full platform compatible implementation.
Let's do migration of typical gradle EE project to the Jakarta EE and look how hard is it. Current JakartaEE version 8.0.0
is fully compatible with JavaEE version 8.0
, that means no need to change project sources, just update dependency from javax:javaee-api:8.0
to jakarta.platform:jakarta.jakartaee-api:8.0.0
updated build.gradle:
apply plugin: 'war'
dependencies {
providedCompile "jakarta.platform:jakarta.jakartaee-api:8.0.0"
}
That is it! Application builds and works well under WF17.0.1
Source code of demo application available on GitHub
19 August 2019
JMX (Java Management Extensions ) - is a technology that provide us possibility to monitoring applications (application servers) by MBeans (Managed Bean) objects.
List of supported MBeans can be obtained by JConsole tool that already included to JDK. As JMX does not provide strong defined communication protocol, - implementations can be different depends on vendor.
For example, to connect to Wildfly Application Server you need to use included in distribution jconsole.sh
script:
<WFLY_HOME>/bin/jconsole.sh
or add <WFLY_HOME>/bin/client/jboss-client.jar
to classpath:
jconsole J-Djava.class.path=$JAVA_HOME\lib\tools.jar;$JAVA_HOME\lib\jconsole.jar;jboss-client.jar
By default, Wildfly uses timeout = 60s
for remote JMX connections, after that connection will terminated:
To change default timeout value, use org.jboss.remoting-jmx.timeout
property:
./jconsole.sh -J-Dorg.jboss.remoting-jmx.timeout=300
But increasing timeouts, is not always good solution. So, lets search for the reason of slowness. To construct list of MBeans, jconsole recursively requests ALL MBeans, that can be extremely slow in case many deployments and many loggers. (Reported issue: WFCORE-3186). Partial solution here is reducing count of log files by changing rotating type from periodic-size-rotating-file-handler
to size-rotating-file-handler
.
Other reason of extremely slowness can be Batch subsystem (JBeret)
. Last one stores a lot of working information in their tables (in memory or on remote DB, depends on configuration). If this tables big enough - it can negative affect performance of server. So, if you, no need for this data then just cleanup this stuff periodically. (for example, every redeploy in case you do it often enough):
TRUNCATE TABLE PARTITION_EXECUTION CASCADE;
TRUNCATE TABLE STEP_EXECUTION CASCADE;
TRUNCATE TABLE JOB_EXECUTION CASCADE;
TRUNCATE TABLE JOB_INSTANCE CASCADE;
From other point of view, obtaining ALL MBeans is not good decision as well. So, just use tooling that allows to find MBeans by path.
08 August 2019
In this post i will share simple and useful gradle template to organize multi module Jakarta EE application. We will implement typical one which consists from REST controller (module1) and some main logic (module2). Big picture of our application architecture is:
So, lets do initialization of project with next gradle template:
settings.gradle:
rootProject.name = 'ee-application-multi-module-gradle-template'
include 'module1'
include 'module2:module2-api', 'module2:module2-core'
root build.gradle:
defaultTasks 'clean', 'build'
subprojects {
ext.libraryVersions = [
javaee : '8.0',
]
defaultTasks 'clean', 'build'
repositories {
jcenter()
}
}
Above, we described initial application structure, where module1 is flat sub project for our controller and module2 is our main logic which consists from API
and Core
sub projects. As controller will use main logic API and we decided to separate application to modules (that means no big enterprise archive) - our sub projects should be simple enough:
module1 build.gradle:
apply plugin: 'war'
dependencies {
compile project(':module2:module2-api')
providedCompile "javax:javaee-api:${libraryVersions.javaee}"
}
module2:module2-api:
apply plugin: 'java'
dependencies {
}
module2:module2-core:
apply plugin: 'war'
dependencies {
compile project(':module2:module2-api')
providedCompile "javax:javaee-api:${libraryVersions.javaee}"
}
Actually, that's it!
Now we can implement our controller like:
@Path("/")
@Stateless
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public class TestEndpoint {
@EJB(lookup = TestService.TEST_SERVICE_JNDI)
TestService testService;
@GET
@Path("/test")
public Response test() {
SomethingDto something = testService.doSomething();
return Response.ok().entity(something.getMessage()).build();
}
In turn, main logic API
contents from Interface
and DTO
:
TestService.java:
public interface TestService {
String TEST_SERVICE_NAME = "test-service";
String TEST_SERVICE_JNDI ="java:global/module2-core/" + TEST_SERVICE_NAME;
SomethingDto doSomething();
}
SomethingDto.java:
public class SomethingDto implements Serializable{
...
}
In the end, main logic Core
contents from the logic that implements API:
TestServiceImpl.java
@Remote(TestService.class)
@Stateless(name = TestService.TEST_SERVICE_NAME)
public class TestServiceImpl implements TestService {
@PersistenceContext
EntityManager entityManager;
@Override
public SomethingDto doSomething() {
TestEntity entity = entityManager.find(TestEntity.class, Long.MAX_VALUE);
return new SomethingDto("Hello Jakarta EE world!");
}
}
Described Jakarta EE application architecture allows us enjoy all power of EE with absolutely transparent inter module interactions and, the same time, stay close to micro service design - as we have no limits with using one container for all modules.
Source code of this demo available on GitHub
07 August 2019
You can send different signals to your application using kill -l command. By default kill will sent TERM
signal. Java by default catches some types of signals, for example
kill -3 <PID>
will dump Java stack traces to the standard error stream.
Also, you can catch signal
inside your application, like
public class App {
public static void main(String... s) throws Exception {
Signal.handle(new Signal("HUP"), signal -> {
System.out.println(signal.getName() + " (" + signal.getNumber() + ")");
});
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
Thread.sleep(1000l);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}).start();
}
}
List of available signals you can get by executing kill -l
:
kostenko@kostenko:~$ kill -l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
Notice! Not all signals can be catched in application, some of them reserved by OS. For example, if you will try to handle kill -SIGKILL (kill -9) , then you get:
Exception in thread "main" java.lang.IllegalArgumentException: Signal already used by VM or OS: SIGKILL
at java.base/jdk.internal.misc.Signal.handle(Signal.java:173)
at jdk.unsupported/sun.misc.Signal.handle(Signal.java:157)
06 August 2019
Previously i wrote about Wildfly domain mode cluster and load balancing from the box. But what if we would like to do balancing depends on specific server behavior ?
Wildfly subsystem mod_cluster
provide us several predefined metric types to determine best request balancing:
As well you also can configure weight (impact of a metric respect to other metrics) and capacity properties;
Below is example, how to change default based on CPU balancing to balancing based on busyness + CPU:
/subsystem=modcluster/mod-cluster-config=configuration/dynamic-load-provider=configuration/load-metric=cpu:remove()
/subsystem=modcluster/mod-cluster-config=configuration:add-metric(type=busyness,weight=2)
/subsystem=modcluster/mod-cluster-config=configuration:add-metric(type=cpu,weight=1)
If predefined types is not enough, - you can provide custom-load-metric
by implementing org.jboss.modcluster.load.metric.impl.AbstractLoadMetric
. To possibility of using your custom metric,- you need to copy packaged JAR to modcluster
module and update module.xml
. Now you can use your custom metric with your configuration like
<custom-load-metric class="org.kostenko.examples.wldfly.modcluster.MyBalancingMetric">