Sunday, November 13, 2016

Programmatically created report with custom datasource in JasperServer

Sometimes there is need to create report programmatically. So let's do this in JasperServer.
Create empty jrxml file and save as dynamic.jrxml
<?xml version="1.0" encoding="UTF-8"?>
<jasperReport xmlns="http://jasperreports.sourceforge.net/jasperreports" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xsi:schemaLocation="http://jasperreports.sourceforge.net/jasperreports http://jasperreports.sourceforge.net/xsd/jasperreport.xsd" 
name="dynamic" pageWidth="595" pageHeight="842" columnWidth="555" leftMargin="20" rightMargin="20" topMargin="20" bottomMargin="20" uuid="aa4ecc63-a8e6-409f-a877-1caf24b98220"/>
Create ant build.xml file
<project name="dynamic" default="deploy" basedir=".">
    <!-- Points to where jasperserver is installed on tomcat or another webapp server.
         Used to find JARs for compiling as well as deploying
    -->
    <property name="webAppDir" value="c:\\Jaspersoft\\jasperreports-server-cp-6.3.0\\apache-tomcat\\webapps\\jasperserver"/>

    <target name="clean">
        <delete dir="classes"/>
    </target>
    <target name="compile">
        <mkdir dir="classes"/>
        <javac srcdir="src" destdir="classes" debug="true">
            <classpath>
                <fileset dir="${webAppDir}/WEB-INF/lib">
                    <include name="*.jar"/>
                </fileset>
                <fileset dir="webapp/WEB-INF/lib">
                    <include name="*.jar"/>
                </fileset>
            </classpath>
        </javac>
    </target>
 <target name="deploy" depends="compile">
        <echo message="webapp src..."/>
  <copy todir="${webAppDir}">
   <fileset dir="webapp"/>
  </copy>
        <!-- copy classes dir -->
        <echo message="classes..."/>
  <copy todir="${webAppDir}/WEB-INF/classes">
   <fileset dir="classes"/>
  </copy>
 </target>
</project>
Create DynamicEngineServiceImpl.java file

package pl.aszywala.dynamic;

import java.awt.Color;
import java.io.InputStream;
import java.util.Random;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.jaspersoft.jasperserver.api.JSExceptionWrapper;
import com.jaspersoft.jasperserver.api.common.domain.ExecutionContext;
import com.jaspersoft.jasperserver.api.engine.jasperreports.service.impl.EngineServiceImpl;
import com.jaspersoft.jasperserver.api.metadata.common.domain.FileResource;

import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JasperReport;
import net.sf.jasperreports.engine.design.JRDesignBand;
import net.sf.jasperreports.engine.design.JRDesignExpression;
import net.sf.jasperreports.engine.design.JRDesignField;
import net.sf.jasperreports.engine.design.JRDesignFrame;
import net.sf.jasperreports.engine.design.JRDesignGroup;
import net.sf.jasperreports.engine.design.JRDesignLine;
import net.sf.jasperreports.engine.design.JRDesignParameter;
import net.sf.jasperreports.engine.design.JRDesignSection;
import net.sf.jasperreports.engine.design.JRDesignStaticText;
import net.sf.jasperreports.engine.design.JRDesignStyle;
import net.sf.jasperreports.engine.design.JRDesignTextField;
import net.sf.jasperreports.engine.design.JRDesignVariable;
import net.sf.jasperreports.engine.design.JasperDesign;
import net.sf.jasperreports.engine.type.CalculationEnum;
import net.sf.jasperreports.engine.type.HorizontalAlignEnum;
import net.sf.jasperreports.engine.type.ModeEnum;
import net.sf.jasperreports.engine.type.PositionTypeEnum;
import net.sf.jasperreports.engine.type.ResetTypeEnum;
import net.sf.jasperreports.repo.JasperDesignCache;

public class DynamicEngineServiceImpl extends EngineServiceImpl {

 protected static final Log log = LogFactory.getLog(DynamicEngineServiceImpl.class);

 protected JasperReport getJasperReport(ExecutionContext context, JasperDesignCache cache, FileResource reportRes,
   String location, boolean inMemoryUnit) {
  if ("/reports/dynamic".equals(location)) {
   return getDynamicReport(context, reportRes);
  } else {
   return super.getJasperReport(context, cache, reportRes, location, inMemoryUnit);
  }
 }

 private JasperReport getDynamicReport(ExecutionContext context, FileResource reportRes) {
  try {
   return compileReport(getJasperDesign(loadReportDesign(getFileResourceDataStream(context, reportRes))));
  } catch (JRException e) {
   log.error(e, e);
   throw new JSExceptionWrapper(e);
  }
 }

 private static JasperDesign getJasperDesign(JasperDesign jasperDesign) throws JRException {
  // JasperDesign
  jasperDesign.setName("NoXmlDesignReport");
  jasperDesign.setPageWidth(595);
  jasperDesign.setPageHeight(842);
  jasperDesign.setColumnWidth(515);
  jasperDesign.setColumnSpacing(0);
  jasperDesign.setLeftMargin(40);
  jasperDesign.setRightMargin(40);
  jasperDesign.setTopMargin(50);
  jasperDesign.setBottomMargin(50);

  // Fonts
  JRDesignStyle normalStyle = new JRDesignStyle();
  normalStyle.setName("Sans_Normal");
  normalStyle
    .setForecolor(new Color(new Random().nextFloat(), new Random().nextFloat(), new Random().nextFloat()));
  normalStyle.setDefault(true);
  normalStyle.setFontName("DejaVu Sans");
  normalStyle.setPdfFontName("Helvetica");
  normalStyle.setPdfEncoding("Cp1252");
  normalStyle.setPdfEmbedded(false);
  jasperDesign.addStyle(normalStyle);

  JRDesignStyle boldStyle = new JRDesignStyle();
  boldStyle.setName("Sans_Bold");
  boldStyle.setFontName("DejaVu Sans");
  boldStyle.setBold(true);
  boldStyle.setPdfFontName("Helvetica-Bold");
  boldStyle.setPdfEncoding("Cp1252");
  boldStyle.setPdfEmbedded(false);
  jasperDesign.addStyle(boldStyle);

  JRDesignStyle italicStyle = new JRDesignStyle();
  italicStyle.setName("Sans_Italic");
  italicStyle.setFontName("DejaVu Sans");
  italicStyle.setItalic(true);
  italicStyle.setPdfFontName("Helvetica-Oblique");
  italicStyle.setPdfEncoding("Cp1252");
  italicStyle.setPdfEmbedded(false);
  jasperDesign.addStyle(italicStyle);

  // Parameters
  JRDesignParameter parameter = new JRDesignParameter();
  parameter.setName("ReportTitle");
  parameter.setValueClass(java.lang.String.class);
  jasperDesign.addParameter(parameter);

  parameter = new JRDesignParameter();
  parameter.setName("OrderByClause");
  parameter.setValueClass(java.lang.String.class);
  jasperDesign.addParameter(parameter);

  JRDesignField field = new JRDesignField();
  field.setName("FirstName");
  field.setValueClass(java.lang.String.class);
  jasperDesign.addField(field);

  field = new JRDesignField();
  field.setName("LastName");
  field.setValueClass(java.lang.String.class);
  jasperDesign.addField(field);

  field = new JRDesignField();
  field.setName("Street");
  field.setValueClass(java.lang.String.class);
  jasperDesign.addField(field);

  field = new JRDesignField();
  field.setName("City");
  field.setValueClass(java.lang.String.class);
  jasperDesign.addField(field);

  // Variables
  JRDesignVariable variable = new JRDesignVariable();
  variable.setName("CityNumber");
  variable.setValueClass(java.lang.Integer.class);
  variable.setResetType(ResetTypeEnum.GROUP);
  JRDesignGroup group = new JRDesignGroup();
  group.setName("CityGroup");
  variable.setResetGroup(group);
  variable.setCalculation(CalculationEnum.SYSTEM);
  JRDesignExpression expression = new JRDesignExpression();
  expression.setValueClass(java.lang.Integer.class);
  expression.setText("($V{CityNumber} != null)?(new Integer($V{CityNumber}.intValue() + 1)):(new Integer(1))");
  variable.setInitialValueExpression(expression);
  jasperDesign.addVariable(variable);

  variable = new JRDesignVariable();
  variable.setName("AllCities");
  variable.setValueClass(java.lang.String.class);
  variable.setResetType(ResetTypeEnum.REPORT);
  variable.setCalculation(CalculationEnum.SYSTEM);
  jasperDesign.addVariable(variable);

  // Groups
  group.setMinHeightToStartNewPage(60);
  expression = new JRDesignExpression();
  expression.setValueClass(java.lang.String.class);
  expression.setText("$F{City}");
  group.setExpression(expression);

  JRDesignBand band = new JRDesignBand();
  band.setHeight(20);
  JRDesignTextField textField = new JRDesignTextField();
  textField.setX(0);
  textField.setY(4);
  textField.setWidth(515);
  textField.setHeight(15);
  textField.setBackcolor(new Color(0xC0, 0xC0, 0xC0));
  textField.setMode(ModeEnum.OPAQUE);
  textField.setHorizontalAlignment(HorizontalAlignEnum.LEFT);
  textField.setStyle(boldStyle);
  expression = new JRDesignExpression();
  expression.setValueClass(java.lang.String.class);
  expression.setText("\"  \" + String.valueOf($V{CityNumber}) + \". \" + String.valueOf($F{City})");
  textField.setExpression(expression);
  band.addElement(textField);
  JRDesignLine line = new JRDesignLine();
  line.setX(0);
  line.setY(19);
  line.setWidth(515);
  line.setHeight(0);
  band.addElement(line);
  ((JRDesignSection) group.getGroupHeaderSection()).addBand(band);

  band = new JRDesignBand();
  band.setHeight(20);
  line = new JRDesignLine();
  line.setX(0);
  line.setY(-1);
  line.setWidth(515);
  line.setHeight(0);
  band.addElement(line);
  JRDesignStaticText staticText = new JRDesignStaticText();
  staticText.setX(400);
  staticText.setY(0);
  staticText.setWidth(60);
  staticText.setHeight(15);
  staticText.setHorizontalAlignment(HorizontalAlignEnum.RIGHT);
  staticText.setStyle(boldStyle);
  staticText.setText("Count : ");
  band.addElement(staticText);
  textField = new JRDesignTextField();
  textField.setX(460);
  textField.setY(0);
  textField.setWidth(30);
  textField.setHeight(15);
  textField.setHorizontalAlignment(HorizontalAlignEnum.RIGHT);
  textField.setStyle(boldStyle);
  expression = new JRDesignExpression();
  expression.setValueClass(java.lang.Integer.class);
  expression.setText("$V{CityGroup_COUNT}");
  textField.setExpression(expression);
  band.addElement(textField);
  ((JRDesignSection) group.getGroupFooterSection()).addBand(band);

  jasperDesign.addGroup(group);

  // Title
  band = new JRDesignBand();
  band.setHeight(50);
  line = new JRDesignLine();
  line.setX(0);
  line.setY(0);
  line.setWidth(515);
  line.setHeight(0);
  band.addElement(line);
  textField = new JRDesignTextField();
  textField.setBlankWhenNull(true);
  textField.setX(0);
  textField.setY(10);
  textField.setWidth(515);
  textField.setHeight(30);
  textField.setHorizontalAlignment(HorizontalAlignEnum.CENTER);
  textField.setStyle(normalStyle);
  textField.setFontSize(22);
  expression = new JRDesignExpression();
  expression.setValueClass(java.lang.String.class);
  expression.setText("$P{ReportTitle}");
  textField.setExpression(expression);
  band.addElement(textField);
  jasperDesign.setTitle(band);

  // Page header
  band = new JRDesignBand();
  band.setHeight(20);
  JRDesignFrame frame = new JRDesignFrame();
  frame.setX(0);
  frame.setY(5);
  frame.setWidth(515);
  frame.setHeight(15);
  frame.setForecolor(new Color(0x33, 0x33, 0x33));
  frame.setBackcolor(new Color(0x33, 0x33, 0x33));
  frame.setMode(ModeEnum.OPAQUE);
  band.addElement(frame);
  staticText = new JRDesignStaticText();
  staticText.setX(0);
  staticText.setY(0);
  staticText.setWidth(55);
  staticText.setHeight(15);
  staticText.setForecolor(Color.white);
  staticText.setBackcolor(new Color(0x33, 0x33, 0x33));
  staticText.setMode(ModeEnum.OPAQUE);
  staticText.setHorizontalAlignment(HorizontalAlignEnum.CENTER);
  staticText.setStyle(boldStyle);
  staticText.setText("First Name");
  frame.addElement(staticText);
  staticText = new JRDesignStaticText();
  staticText.setX(55);
  staticText.setY(0);
  staticText.setWidth(205);
  staticText.setHeight(15);
  staticText.setForecolor(Color.white);
  staticText.setBackcolor(new Color(0x33, 0x33, 0x33));
  staticText.setMode(ModeEnum.OPAQUE);
  staticText.setStyle(boldStyle);
  staticText.setText("Last Name");
  frame.addElement(staticText);
  staticText = new JRDesignStaticText();
  staticText.setX(260);
  staticText.setY(0);
  staticText.setWidth(255);
  staticText.setHeight(15);
  staticText.setForecolor(Color.white);
  staticText.setBackcolor(new Color(0x33, 0x33, 0x33));
  staticText.setMode(ModeEnum.OPAQUE);
  staticText.setStyle(boldStyle);
  staticText.setText("Street");
  frame.addElement(staticText);
  jasperDesign.setPageHeader(band);

  // Column header
  band = new JRDesignBand();
  jasperDesign.setColumnHeader(band);

  // Detail
  band = new JRDesignBand();
  band.setHeight(20);
  textField = new JRDesignTextField();
  textField.setX(0);
  textField.setY(4);
  textField.setWidth(50);
  textField.setHeight(15);
  textField.setHorizontalAlignment(HorizontalAlignEnum.RIGHT);
  textField.setStyle(normalStyle);
  expression = new JRDesignExpression();
  expression.setValueClass(java.lang.Integer.class);
  expression.setText("$F{FirstName}");
  textField.setExpression(expression);
  band.addElement(textField);
  textField = new JRDesignTextField();
  textField.setStretchWithOverflow(true);
  textField.setX(55);
  textField.setY(4);
  textField.setWidth(200);
  textField.setHeight(15);
  textField.setPositionType(PositionTypeEnum.FLOAT);
  textField.setStyle(normalStyle);
  expression = new JRDesignExpression();
  expression.setValueClass(java.lang.String.class);
  expression.setText("$F{LastName}");
  textField.setExpression(expression);
  band.addElement(textField);
  textField = new JRDesignTextField();
  textField.setStretchWithOverflow(true);
  textField.setX(260);
  textField.setY(4);
  textField.setWidth(255);
  textField.setHeight(15);
  textField.setPositionType(PositionTypeEnum.FLOAT);
  textField.setStyle(normalStyle);
  expression = new JRDesignExpression();
  expression.setValueClass(java.lang.String.class);
  expression.setText("$F{Street}");
  textField.setExpression(expression);
  band.addElement(textField);
  line = new JRDesignLine();
  line.setX(0);
  line.setY(19);
  line.setWidth(515);
  line.setHeight(0);
  line.setForecolor(new Color(0x80, 0x80, 0x80));
  line.setPositionType(PositionTypeEnum.FLOAT);
  band.addElement(line);
  ((JRDesignSection) jasperDesign.getDetailSection()).addBand(band);

  // Column footer
  band = new JRDesignBand();
  jasperDesign.setColumnFooter(band);

  // Page footer
  band = new JRDesignBand();
  jasperDesign.setPageFooter(band);

  // Summary
  band = new JRDesignBand();
  jasperDesign.setSummary(band);

  return jasperDesign;
 }
}
When report location equals to /reports/dynamic then report is generated programmatically. Every time when report is generated font is changed to random color so you can see that report is changing. </ br> Open js.spring.properties file. It is located in WEB-INF folder of the JasperServer web application directory in my case is c:\Jaspersoft\jasperreports-server-cp-6.3.0\apache-tomcat\webapps\jasperserver\WEB-INF\ and change bean.class.engineService to:
bean.class.engineService=pl.aszywala.dynamic.DynamicEngineServiceImpl
Let's create custom datasource for the report. Create DynamicDataSource class
package pl.aszywala.dynamic;

import net.sf.jasperreports.engine.JRDataSource;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JRField;

public class DynamicDataSource implements JRDataSource {

 private Object[][] data = { { "Katowice", "Jan", "Kowalski", "Staszica 1" },
   { "Katowice", "Piotr", "Nowak", "Warszawska 11" },
   { "New York", "Andrew", "May", "172 Seventh Av." },
   { "New York", "Sylvia", "Ott", "361 College Av." },
   { "New York", "Bill", "King", "546 College Av." },
   { "Paris", "Sylvia", "Steel", "269 College Av." },
   { "Paris", "Sylvia", "Fuller", "158 - 20th Ave." },
   { "Paris", "Laura", "Miller", "294 Seventh Av." },
   { "San Francisco","Robert", "White", "549 Seventh Av." },
   { "San Francisco", "James", "Peterson", "231 Upland Pl." } };

 private int index = -1;

 public DynamicDataSource() {
 }

 public boolean next() throws JRException {
  return (++index < data.length);
 }

 public Object getFieldValue(JRField field) throws JRException {
  Object value = null;

  String fieldName = field.getName();

  if ("City".equals(fieldName)) {
   value = data[index][0];
  } else if ("FirstName".equals(fieldName)) {
   value = data[index][1];
  } else if ("LastName".equals(fieldName)) {
   value = data[index][2];
  } else if ("Street".equals(fieldName)) {
   value = data[index][3];
  }

  return value;
 }

}
Create DynamicDataSourceService class
package pl.aszywala.dynamic;

import java.util.Map;

import net.sf.jasperreports.engine.JRDataSource;
import net.sf.jasperreports.engine.JRParameter;

import com.jaspersoft.jasperserver.api.metadata.common.service.RepositoryService;
import com.jaspersoft.jasperserver.api.metadata.jasperreports.service.ReportDataSourceService;

public class DynamicDataSourceService implements ReportDataSourceService {

 JRDataSource ds;
 private RepositoryService repository;
 private Map propertyMap;
 
 public Map getPropertyMap() {
  return propertyMap;
 }

 public void setPropertyMap(Map propertyMap) {
  this.propertyMap = propertyMap;
 }

 public DynamicDataSourceService() {
  this.ds = new DynamicDataSource();
 }
 
 public DynamicDataSourceService(JRDataSource ds) {
  this.ds = ds;
 }
 
 public void closeConnection() {
  // Do nothing
 }

 public void setReportParameterValues(Map parameterValues) {
  parameterValues.put(JRParameter.REPORT_DATA_SOURCE, ds);
 }

 public RepositoryService getRepository() {
  return repository;
 }

 public void setRepository(RepositoryService repository) {
  this.repository = repository;
 }

}
In WEB-INF/bundles directory create dynamic.properties file
dynamicDataSource.name=Dynamic Data Source
SEARCH_dynamicDataSource=Dynamic Data Source
resource.dynamicDataSource.label=Dynamic Data Source
The last thing is to create applicationContext-dynamic.xml file
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
       
 <bean id="dynamicDataSource" class="com.jaspersoft.jasperserver.api.engine.jasperreports.util.CustomDataSourceDefinition">
  <property name="factory" ref="customDataSourceServiceFactory"/>
  <property name="name" value="dynamicDataSource"/>
  <property name="serviceClassName" value="pl.aszywala.dynamic.DynamicDataSourceService"/> 
  <property name="propertyDefinitions">
   <list/>    
  </property>
  <property name="queryExecuterMap">
   <map/>
  </property> 
 </bean>

    <bean class="com.jaspersoft.jasperserver.api.common.util.spring.GenericBeanUpdater">
        <property name="definition" ref="addMessageCatalog"/>
        <property name="value">
            <list>
                <value>WEB-INF/bundles/dynamic</value>
            </list>
        </property>
        <property name="valueType" value="stringList"/>
    </bean>
</beans>
Run ant command in directory where your build.xml file is placed. It should build the project and copy files to the JasperServer webapp directory. Restart JasperServer and login to the dashboard.
Add new datasource
Add new report
And connect it to the dynamicDS datasource
Run the raport
Source code for the blog post is available on the GitHub

Saturday, November 5, 2016

mod_cluster as a Service Discovery with Wildfly and Docker

In this blog post I'll show how to deploy microservices which uses mod_cluster instead Service Discovery. First let's create Dockerfile with mod_cluster as load balancer:
FROM fedora
RUN dnf -y update
RUN dnf -y install httpd
RUN dnf -y install mod_cluster
RUN dnf -y install mod_ssl
RUN dnf clean all

RUN sed -i "s|LoadModule proxy_balancer_module|#LoadModule proxy_balancer_module|" /etc/httpd/conf.modules.d/00-proxy.conf

ADD mod_cluster.conf /etc/httpd/conf.d/mod_cluster.conf
RUN mkdir /etc/httpd/conf/certs
RUN openssl req \
    -new \
    -newkey rsa:4096 \
    -days 365 \
    -nodes \
    -x509 \
    -subj "/C=PL/ST=Slaskie/L=Katowice/O=aszywala/OU=aszywala/CN=Andrzej Szywala" \
    -keyout www.aszywala.key \
    -out www.aszywala.crt
EXPOSE 80
EXPOSE 443
CMD ["/sbin/httpd", "-DFOREGROUND"]

Next I'll create mod_cluster.conf
LoadModule proxy_cluster_module modules/mod_proxy_cluster.so
LoadModule cluster_slotmem_module modules/mod_cluster_slotmem.so
LoadModule manager_module modules/mod_manager.so
LoadModule advertise_module modules/mod_advertise.so

<IfModule manager_module>

  Maxhost 100

  <VirtualHost *:80>
    <Directory />
        Require all granted
    </Directory>
    <Location /mod_cluster_manager>
        SetHandler mod_cluster-manager
        Require all granted
    </Location>
    <Location /server-status>
        SetHandler server-status
    </Location>

    KeepAliveTimeout 60
    ManagerBalancerName mycluster
    ServerAdvertise On
    EnableMCPMReceive On
  </VirtualHost>
</IfModule>

Build Dockerfile
docker build -t lb .
Create docker network
docker network create mynet
Run container with load balancer
docker run --name lb -it --rm --net mynet -p 80:80 -p 443:443 lb
You can open web browser and check if mod_cluster works http://localhost/mod_cluster_manager
Create Dockerfile with micro service application.
FROM jboss/wildfly
MAINTAINER Andrzej Szywala 
ADD customization $JBOSS_HOME
RUN /opt/jboss/wildfly/bin/jboss-cli.sh --file=/opt/jboss/wildfly/wildfly_conf.txt
# FIX for Error: WFLYCTL0056: Could not rename /opt/jboss/wildfly/standalone/configuration/standalone_xml_history/current...
RUN rm -rf /opt/jboss/wildfly/standalone/configuration/standalone_xml_history/current/*
ADD hellojee.war $JBOSS_HOME/standalone/deployments/hellojee.war
wildfly_conf.txt file configures mod_cluster and ajp
embed-server --server-config=standalone.xml
batch
/socket-binding-group=standard-sockets/remote-destination-outbound-socket-binding=lb:add(host="lb",port="80")
/extension=org.jboss.as.modcluster:add
/subsystem=undertow/server=default-server/ajp-listener=ajp:add(socket-binding=ajp, scheme="http")
/:composite(steps=[{"operation" => "add", "address" => [ ("subsystem" => "modcluster") ]},{ "operation" => "add", "address" => [ ("subsystem" => "modcluster"), ("mod-cluster-config" => "configuration") ], "connector" => "ajp", "balancer" => "hellojee", "advertise" => "false", "proxies" => ["lb"] }])
# Execute the batch
run-batch
reload
stop-embedded-server
quit
Build Dockerfile
docker build -t hellojee_cluster .
Run container
docker run --name hellojee_cluster -it --rm --net mynet hellojee_cluster
Check if application registers in loadbalancer http://localhost/mod_cluster_manager

Open browser http://localhost/hellojee/resources/hello. You should see response from application
{"database":"H2 - 1.3.173 (2013-07-28)","host":"0461b41cff7f (172.20.0.3)","hello":["Hello","Witaj","Hallo","Hej","Ahoj","Bonjour","Hola"]}
When you stop the container application will be automatically deregistered from mod_cluster. When you kill the container mod_cluster will notice that and change application status to NOTOK.

Sources for this post can be found on Github https://github.com/andrzejszywala/docker-images/tree/master/hellojee/mod_cluster