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

Sunday, October 9, 2016

Change database without changing code using default datasource and Docker.

In this blog post we will consider the use case that we want to use different databases for our system but we don't want to change any line of code or any file in built application. In this example we will use a simple application with a REST interface. The source code is available on github https://github.com/andrzejszywala/hellojee

In persistence.xml we don't specify datasource so application will use default datasource which is provided by the container.
<persistence version="2.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://xmlns.jcp.org/xml/ns/persistence" 
    xsi:schemalocation="http://xmlns.jcp.org/xml/ns/persistence
    http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
  <persistence-unit name="hellojee" transaction-type="JTA">
    <properties>
      <property name="javax.persistence.schema-generation.database.action" value="drop-and-create">
    </property></properties>
  </persistence-unit>
</persistence>

Now let's create first Dockerfile
FROM jboss/wildfly
ADD hellojee.war $JBOSS_HOME/standalone/deployments/hellojee.war

This Dockerfile extends jboss/wildfly image and adds hellojee.war to deployments directory. Next let's build and run the image
docker build -t andrzejszywala/hellojee .
docker run -d -p 8080:8080 --name hellojee andrzejszywala/hellojee
Open browser http://localhost:8080/hellojee/resources/hello and verify that the application works. In the response you should see json object with database name, host name and some sample data from Hello table.
{"database":"H2 - 1.3.173 (2013-07-28)","host":"597840afb246 (172.17.0.2)","hello": "Hello","Witaj","Hallo","Hej","Ahoj","Bonjour","Hola"]}
Stop container
docker stop hellojee
Now we will create docker image that connects to mysql databse. Dockerfile is available on https://github.com/andrzejszywala/docker-images/tree/master/hellojee/mysql
FROM jboss/wildfly
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
the most important part is wildfly_conf.txt file which configures mysql module and sets mysql datasource as default so wildfly will use it in our application.
embed-server --server-config=standalone.xml
batch
# add mysql module
module add --name=com.mysql --resources=/opt/jboss/wildfly/mysql-connector-java-5.1.34.jar --dependencies=javax.api,javax.transaction.api
# driver configuration
/subsystem=datasources/jdbc-driver=mysql:add(driver-name=mysql,driver-module-name=com.mysql,driver-xa-datasource-class-name=com.mysql.jdbc.jdbc2.optional.MysqlXADataSource)
# add new datasource
data-source add --name=mysqlDS --driver-name=mysql --jndi-name=java:jboss/datasources/MySQLDS --connection-url=jdbc:mysql://mysqldb:${env.DB_PORT}/sample?useUnicode=true&characterEncoding=UTF-8 --user-name=mysql --password=mysql --use-ccm=false --max-pool-size=25 --blocking-timeout-wait-millis=5000 --enabled=true
# set default datasource
/subsystem=ee/service=default-bindings:write-attribute(name="datasource", value="java:jboss/datasources/MySQLDS")
# Execute the batch
run-batch
reload
stop-embedded-server
quit
Note that mysql host name is mysqldb. This will be the name of the mysql container and after run containers in the same network we will be able to connect to the database. Now we can build docker image
docker build -t andrzejszywala/hellojee-mysql .
Create docker network
docker network create mynet
Run mysql container
docker run --name mysqldb --net mynet -e "MYSQL_ROOT_PASSWORD=mysql" -e "MYSQL_DATABASE=sample" -e "MYSQL_USER=mysql" -e "MYSQL_PASSWORD=mysql"-d mysql:latest
And finally run the application container
docker run -d -p 8080:8080 -e "DB_PORT=3306" --name hellojee-mysql --net mynet andrzejszywala/hellojee-mysql
Open browser http://localhost:8080/hellojee/resources/hello. You should see something like this:
{"database":"MySQL - 5.7.15","host":"8acd618e942e (172.20.0.3)","hello":["Hello","Witaj","Hallo","Hej","Ahoj","Bonjour","Hola"]}
As you can see database is MySQL. JEE 7 gives us ability to build once and deploy anywhere and if we do not go beyond standard we can easily change servers and databases. All files used in this blog post are available on github https://github.com/andrzejszywala/docker-images/tree/master/hellojee

Thursday, July 14, 2016

ng2-admin + ag-grid

This blog will show how to add ag-grid to ng2-admin.
ag-grid is a JavaScript Data Grid for Building Enterprise Web Applications.
ng2-admin is a admin template made by Akveo team.

Install required tools (do it as administrator)
npm install --global webpack
npm install --global webpack-dev-server
npm install --global typings
npm install --global typescript
npm install --global bower

Clone ng2-admin git repository
git clone https://github.com/akveo/ng2-admin.git
cd ng2-admin 

Install dependencies
npm install

Finally run local dev server
npm start

Open browser at http://localhost:3000


Let's add ag-grid

Install ag-grid dependencies
npm install ag-grid ag-grid-ng2 --save


Open vs code 
code .

Edit index.html and add links to ag-grid css files
  <link href="assets/ag-grid/styles/ag-grid.css" rel="stylesheet"></link>
  <link href="assets/ag-grid/styles/theme-blue.css" rel="stylesheet"></link>



Copy ag-grid.css and theme-blue.css to assets/ag-grid/styles folder

Let's add sample gird to Basic Tables page. Grid is based on sample https://github.com/ceolter/ag-grid-ng2-example
Copy content of images folder to src/assets/img
Create folder agTable in src\app\pages\tables\components\basicTables\components directory.
Create agTable.html file
<div class="horizontal-scroll">
<div style="width: 100%;">

    <div style="padding: 4px;">
        <div style="float: right;">
            <input (keyup)="onQuickFilterChanged($event)" type="text" id="quickFilterInput" placeholder="Type text to filter..."/>

            <button [disabled]="!showGrid" (click)="showGrid=false" class="btn btn-primary btn-xs">Destroy Grid</button>
            
            <button [disabled]="showGrid" (click)="showGrid=true" class="btn btn-primary btn-xs">Create Grid</button>
        </div>
        <div>
            <b>Employees Skills and Contact Details</b>
            {{rowCount}}
        </div>
    </div>
    <div style="clear: both;"></div>

    <div *ngIf="showGrid">

        <!-- Because we are using the Angular ID (ie #ag-grid marker), we have to have all the items that use
             that marker inside the same ng-if as the grid -->

        <div style="padding: 4px;" class="toolbar">
            <span>
                Grid API:
                <button (click)="agGrid.api.selectAll()" class="btn btn-primary btn-xs">Select All</button>
                <button (click)="agGrid.api.deselectAll()" class="btn btn-primary btn-xs">Clear Selection</button>
            </span>
            <span style="margin-left: 20px;">
                Column API:
                <button (click)="agGrid.columnApi.setColumnVisible('country', false)" class="btn btn-primary btn-xs">Hide Country Column</button>
                <button (click)="agGrid.columnApi.setColumnVisible('country', true)" class="btn btn-primary btn-xs">Show Country Column</button>
            </span>
        </div>
        <div style="clear: both;"></div>
        <div style="padding: 4px;" class="toolbar">
            <label>
                <input type="checkbox" (change)="showToolPanel=$event.target.checked"/>
                Show Tool Panel
            </label>
            <button (click)="createRowData()" class="btn btn-primary btn-xs">Refresh Data</button>
        </div>
        <div style="clear: both;"></div>

        <ag-grid-ng2 #agGrid style="width: 100%; height: 350px;" class="ag-blue"

                     [gridOptions]="gridOptions"
                     [columnDefs]="columnDefs"
                     [showToolPanel]="showToolPanel"
                     [rowData]="rowData"

                     enableColResize
                     enableSorting
                     enableFilter
                     groupHeaders
                     suppressRowClickSelection
                     toolPanelSuppressGroups
                     toolPanelSuppressValues
                     debug
                     rowHeight="22"
                     rowSelection="multiple"

                     (modelUpdated)="onModelUpdated()"
                     (cellClicked)="onCellClicked($event)"
                     (cellDoubleClicked)="onCellDoubleClicked($event)"
                     (cellContextMenu)="onCellContextMenu($event)"
                     (cellValueChanged)="onCellValueChanged($event)"
                     (cellFocused)="onCellFocused($event)"
                     (rowSelected)="onRowSelected($event)"
                     (selectionChanged)="onSelectionChanged()"
                     (beforeFilterChanged)="onBeforeFilterChanged()"
                     (afterFilterChanged)="onAfterFilterChanged()"
                     (filterModified)="onFilterModified()"
                     (beforeSortChanged)="onBeforeSortChanged()"
                     (afterSortChanged)="onAfterSortChanged()"
                     (virtualRowRemoved)="onVirtualRowRemoved($event)"
                     (rowClicked)="onRowClicked($event)"
                     (ready)="onReady($event)"

                     (columnEverythingChanged)="onColumnEvent($event)"
                     (columnRowGroupChanged)="onColumnEvent($event)"
                     (columnValueChanged)="onColumnEvent($event)"
                     (columnMoved)="onColumnEvent($event)"
                     (columnVisible)="onColumnEvent($event)"
                     (columnGroupOpened)="onColumnEvent($event)"
                     (columnResized)="onColumnEvent($event)"
                     (columnPinnedCountChanged)="onColumnEvent($event)">
        </ag-grid-ng2>
    </div>

</div>
</div>

Create agTable.components.ts file 
import {Component} from '@angular/core';

import {BaAppPicturePipe} from '../../../../../../theme/pipes';
import {BasicTablesService} from '../../basicTables.service';
import {AgGridNg2} from 'ag-grid-ng2/main';
import {GridOptions} from 'ag-grid/main';
import ProficiencyFilter from './proficiencyFilter';
import SkillFilter from './skillFilter';
import RefData from './refData';


@Component({
     selector: 'ag-table',
  template: require('./agTable.html'),
    directives: [AgGridNg2],
    styles: ['.toolbar button {margin: 2px; padding: 0px;}'],
})
export class AgTable {

    private gridOptions: GridOptions;
    private showGrid: boolean;
    private rowData: any[];
    private columnDefs: any[];
    private rowCount: string;

    constructor() {
        // we pass an empty gridOptions in, so we can grab the api out
        this.gridOptions = <GridOptions>{};
        this.createRowData();
        this.createColumnDefs();
        this.showGrid = true;
    }

    private createRowData() {
        var rowData: any[] = [];

        for (var i = 0; i < 10000; i++) {
            var countryData = RefData.countries[i % RefData.countries.length];
            rowData.push({
                name: RefData.firstNames[i % RefData.firstNames.length] + ' ' + RefData.lastNames[i % RefData.lastNames.length],
                skills: {
                    android: Math.random() < 0.4,
                    html5: Math.random() < 0.4,
                    mac: Math.random() < 0.4,
                    windows: Math.random() < 0.4,
                    css: Math.random() < 0.4
                },
                address: RefData.addresses[i % RefData.addresses.length],
                years: Math.round(Math.random() * 100),
                proficiency: Math.round(Math.random() * 100),
                country: countryData.country,
                continent: countryData.continent,
                language: countryData.language,
                mobile: createRandomPhoneNumber(),
                landline: createRandomPhoneNumber()
            });
        }

        this.rowData = rowData;
    }

    private createColumnDefs() {
        this.columnDefs = [
            {headerName: '#', width: 30, checkboxSelection: true, suppressSorting: true,
                suppressMenu: true, pinned: true},
            {
                headerName: 'Employee',
                children: [
                    {headerName: "Name", field: "name",
                        width: 150, pinned: true},
                    {headerName: "Country", field: "country", width: 150,
                        cellRenderer: countryCellRenderer, pinned: true,
                        filterParams: {cellRenderer: countryCellRenderer, cellHeight: 20}},
                ]
            },
            {
                headerName: 'IT Skills',
                children: [
                    {headerName: "Skills", width: 125, suppressSorting: true, cellRenderer: skillsCellRenderer, filter: SkillFilter},
                    {headerName: "Proficiency", field: "proficiency", width: 120, cellRenderer: percentCellRenderer, filter: ProficiencyFilter},
                ]
            },
            {
                headerName: 'Contact',
                children: [
                    {headerName: "Mobile", field: "mobile", width: 150, filter: 'text'},
                    {headerName: "Land-line", field: "landline", width: 150, filter: 'text'},
                    {headerName: "Address", field: "address", width: 500, filter: 'text'}
                ]
            }
        ];
    }

    private calculateRowCount() {
        if (this.gridOptions.api && this.rowData) {
            var model = this.gridOptions.api.getModel();
            var totalRows = this.rowData.length;
            var processedRows = model.getRowCount();
            this.rowCount = processedRows.toLocaleString() + ' / ' + totalRows.toLocaleString();
        }
    }

    private onModelUpdated() {
        console.log('onModelUpdated');
        this.calculateRowCount();
    }

    private onReady() {
        console.log('onReady');
        this.calculateRowCount();
    }

    private onCellClicked($event) {
        console.log('onCellClicked: ' + $event.rowIndex + ' ' + $event.colDef.field);
    }

    private onCellValueChanged($event) {
        console.log('onCellValueChanged: ' + $event.oldValue + ' to ' + $event.newValue);
    }

    private onCellDoubleClicked($event) {
        console.log('onCellDoubleClicked: ' + $event.rowIndex + ' ' + $event.colDef.field);
    }

    private onCellContextMenu($event) {
        console.log('onCellContextMenu: ' + $event.rowIndex + ' ' + $event.colDef.field);
    }

    private onCellFocused($event) {
        console.log('onCellFocused: (' + $event.rowIndex + ',' + $event.colIndex + ')');
    }

    private onRowSelected($event) {
        // taking out, as when we 'select all', it prints to much to the console!!
        // console.log('onRowSelected: ' + $event.node.data.name);
    }

    private onSelectionChanged() {
        console.log('selectionChanged');
    }

    private onBeforeFilterChanged() {
        console.log('beforeFilterChanged');
    }

    private onAfterFilterChanged() {
        console.log('afterFilterChanged');
    }

    private onFilterModified() {
        console.log('onFilterModified');
    }

    private onBeforeSortChanged() {
        console.log('onBeforeSortChanged');
    }

    private onAfterSortChanged() {
        console.log('onAfterSortChanged');
    }

    private onVirtualRowRemoved($event) {
        // because this event gets fired LOTS of times, we don't print it to the
        // console. if you want to see it, just uncomment out this line
        // console.log('onVirtualRowRemoved: ' + $event.rowIndex);
    }

    private onRowClicked($event) {
        console.log('onRowClicked: ' + $event.node.data.name);
    }

    private onQuickFilterChanged($event) {
        this.gridOptions.api.setQuickFilter($event.target.value);
    }

    // here we use one generic event to handle all the column type events.
    // the method just prints the event name
    private onColumnEvent($event) {
        console.log('onColumnEvent: ' + $event);
    }

}

function skillsCellRenderer(params) {
    var data = params.data;
    var skills = [];
    RefData.IT_SKILLS.forEach(function (skill) {
        if (data && data.skills && data.skills[skill]) {
            skills.push('<img src="assets/img/skills/' + skill + '.png" width="16px" title="' + skill + '" />');
        }
    });
    return skills.join(' ');
}

function countryCellRenderer(params) {
    var flag = "<img border='0' width='15' height='10' style='margin-bottom: 2px' src='assets/img/flags/" + RefData.COUNTRY_CODES[params.value] + ".png'>";
    return flag + " " + params.value;
}

function createRandomPhoneNumber() {
    var result = '+';
    for (var i = 0; i < 12; i++) {
        result += Math.round(Math.random() * 10);
        if (i === 2 || i === 5 || i === 8) {
            result += ' ';
        }
    }
    return result;
}

function percentCellRenderer(params) {
    var value = params.value;

    var eDivPercentBar = document.createElement('div');
    eDivPercentBar.className = 'div-percent-bar';
    eDivPercentBar.style.width = value + '%';
    if (value < 20) {
        eDivPercentBar.style.backgroundColor = 'red';
    } else if (value < 60) {
        eDivPercentBar.style.backgroundColor = '#ff9900';
    } else {
        eDivPercentBar.style.backgroundColor = '#00A000';
    }

    var eValue = document.createElement('div');
    eValue.className = 'div-percent-value';
    eValue.innerHTML = value + '%';

    var eOuterDiv = document.createElement('div');
    eOuterDiv.className = 'div-outer-div';
    eOuterDiv.appendChild(eValue);
    eOuterDiv.appendChild(eDivPercentBar);

    return eOuterDiv;
}

Create index.ts file
export * from './agTable.component';

Create proficiencyFilter.ts file
var FILTER_TITLE =
    '<div style="text-align: center; background: lightgray; width: 100%; display: block; border-bottom: 1px solid grey;">' +
    '<b>TITLE_NAME</b>' +
    '</div>';

var PROFICIENCY_TEMPLATE =
    '<label style="padding-left: 4px;">' +
    '<input type="radio" name="RANDOM"/>' +
    'PROFICIENCY_NAME' +
    '</label>';

var PROFICIENCY_NONE = 'none';
var PROFICIENCY_ABOVE40 = 'above40';
var PROFICIENCY_ABOVE60 = 'above60';
var PROFICIENCY_ABOVE80 = 'above80';

var PROFICIENCY_NAMES = ['No Filter', 'Above 40%', 'Above 60%', 'Above 80%'];
var PROFICIENCY_VALUES = [PROFICIENCY_NONE, PROFICIENCY_ABOVE40, PROFICIENCY_ABOVE60, PROFICIENCY_ABOVE80];

export default class ProficiencyFilter {

    private filterChangedCallback: Function;
    private selected: string;
    private valueGetter: Function;

    private init(params) {
        this.filterChangedCallback = params.filterChangedCallback;
        this.selected = PROFICIENCY_NONE;
        this.valueGetter = params.valueGetter;
    }

    private getGui() {
        var eGui = document.createElement('div');
        var eInstructions = document.createElement('div');
        eInstructions.innerHTML = FILTER_TITLE.replace('TITLE_NAME', 'Custom Proficiency Filter');
        eGui.appendChild(eInstructions);

        var random = '' + Math.random();

        var that = this;
        PROFICIENCY_NAMES.forEach( function (name, index) {
            var eFilter = document.createElement('div');
            var html = PROFICIENCY_TEMPLATE.replace('PROFICIENCY_NAME', name).replace('RANDOM', random);
            eFilter.innerHTML = html;
            var eRadio = <HTMLInputElement> eFilter.querySelector('input');
            if (index === 0) {
                eRadio.checked = true;
            }
            eGui.appendChild(eFilter);

            eRadio.addEventListener('click', function () {
                that.selected = PROFICIENCY_VALUES[index];
                that.filterChangedCallback();
            });
        });

        return eGui;
    }

    private doesFilterPass(params) {

        var value = this.valueGetter(params);
        var valueAsNumber = parseFloat(value);

        switch (this.selected) {
            case PROFICIENCY_ABOVE40 : return valueAsNumber >= 40;
            case PROFICIENCY_ABOVE60 : return valueAsNumber >= 60;
            case PROFICIENCY_ABOVE80 : return valueAsNumber >= 80;
            default : return true;
        }

    }

    private isFilterActive() {
        return this.selected !== PROFICIENCY_NONE;
    }

}

Create refData.ts file
export default class RefData {

    static IT_SKILLS = ['android', 'css', 'html5', 'mac', 'windows'];
    static IT_SKILLS_NAMES = ['Android', 'CSS', 'HTML 5', 'Mac', 'Windows'];


    static firstNames = ["Sophie", "Isabelle", "Emily", "Olivia", "Lily", "Chloe", "Isabella",
        "Amelia", "Jessica", "Sophia", "Ava", "Charlotte", "Mia", "Lucy", "Grace", "Ruby",
        "Ella", "Evie", "Freya", "Isla", "Poppy", "Daisy", "Layla"];
    static lastNames = ["Beckham", "Black", "Braxton", "Brennan", "Brock", "Bryson", "Cadwell",
        "Cage", "Carson", "Chandler", "Cohen", "Cole", "Corbin", "Dallas", "Dalton", "Dane",
        "Donovan", "Easton", "Fisher", "Fletcher", "Grady", "Greyson", "Griffin", "Gunner",
        "Hayden", "Hudson", "Hunter", "Jacoby", "Jagger", "Jaxon", "Jett", "Kade", "Kane",
        "Keating", "Keegan", "Kingston", "Kobe"];

    static COUNTRY_CODES = {
        Ireland: "ie",
        Spain: "es",
        "United Kingdom": "gb",
        France: "fr",
        Germany: "de",
        Sweden: "se",
        Italy: "it",
        Greece: "gr",
        Iceland: "is",
        Portugal: "pt",
        Malta: "mt",
        Norway: "no",
        Brazil: "br",
        Argentina: "ar",
        Colombia: "co",
        Peru: "pe",
        Venezuela: "ve",
        Uruguay: "uy"
    };

    static countries = [
        {country: "Ireland", continent: "Europe", language: "English"},
        {country: "Spain", continent: "Europe", language: "Spanish"},
        {country: "United Kingdom", continent: "Europe", language: "English"},
        {country: "France", continent: "Europe", language: "French"},
        {country: "Germany", continent: "Europe", language: "(other)"},
        {country: "Sweden", continent: "Europe", language: "(other)"},
        {country: "Norway", continent: "Europe", language: "(other)"},
        {country: "Italy", continent: "Europe", language: "(other)"},
        {country: "Greece", continent: "Europe", language: "(other)"},
        {country: "Iceland", continent: "Europe", language: "(other)"},
        {country: "Portugal", continent: "Europe", language: "Portuguese"},
        {country: "Malta", continent: "Europe", language: "(other)"},
        {country: "Brazil", continent: "South America", language: "Portuguese"},
        {country: "Argentina", continent: "South America", language: "Spanish"},
        {country: "Colombia", continent: "South America", language: "Spanish"},
        {country: "Peru", continent: "South America", language: "Spanish"},
        {country: "Venezuela", continent: "South America", language: "Spanish"},
        {country: "Uruguay", continent: "South America", language: "Spanish"}
    ];

    static addresses = [
        '1197 Thunder Wagon Common, Cataract, RI, 02987-1016, US, (401) 747-0763',
        '3685 Rocky Glade, Showtucket, NU, X1E-9I0, CA, (867) 371-4215',
        '3235 High Forest, Glen Campbell, MS, 39035-6845, US, (601) 638-8186',
        '2234 Sleepy Pony Mall , Drain, DC, 20078-4243, US, (202) 948-3634',
        '2722 Hazy Turnabout, Burnt Cabins, NY, 14120-5642, US, (917) 604-6597',
        '6686 Lazy Ledge, Two Rock, CA, 92639-3020, US, (619) 901-9911',
        '2000 Dewy Limits, Wacahoota, NF, A4L-2V9, CA, (709) 065-3959',
        '7710 Noble Pond Avenue, Bolivia, RI, 02931-1842, US, (401) 865-2160',
        '3452 Sunny Vale, Pyro, ON, M8V-4Z0, CA, (519) 072-8609',
        '4402 Dusty Cove, Many Farms, UT, 84853-8223, US, (435) 518-0673',
        '5198 Silent Parade, Round Bottom, MD, 21542-9798, US, (301) 060-7245',
        '8550 Shady Moor, Kitty Fork, CO, 80941-6207, US, (303) 502-3767',
        '2131 Old Dell, Merry Midnight, AK, 99906-8842, US, (907) 369-2206',
        '7390 Harvest Crest, Mosquito Crossing, RI, 02957-6116, US, (401) 463-6348',
        '874 Little Point, Hot Coffee, BC, V3U-2P6, CA, (250) 706-9207',
        '8834 Stony Pioneer Heights, Newlove, OR, 97419-8670, US, (541) 408-2213',
        '9829 Grand Beach, Flint, UT, 84965-9900, US, (435) 700-5161',
        '3799 Cozy Blossom Ramp, Ptarmigan, MS, 38715-0313, US, (769) 740-1526',
        '3254 Silver Island Loop, Maunaloa, DE, 19869-3169, US, (302) 667-7671',
        '1081 Middle Wood, Taylors Gut Landing, OR, 97266-2873, US, (541) 357-6310',
        '1137 Umber Trail, Shacktown, NW, X3U-5Y8, CA, (867) 702-6883',
        '9914 Hidden Bank, Wyoming, MO, 64635-9665, US, (636) 280-4192',
        '7080 Misty Nectar Townline, Coward, AB, T9U-3N4, CA, (403) 623-2838',
        '1184 Wishing Grounds, Vibank, NW, X7D-0V9, CA, (867) 531-2730',
        '126 Easy Pointe, Grandview Beach, KY, 40928-9539, US, (502) 548-0956',
        '6683 Colonial Street, Swan River, BC, V1A-9I8, CA, (778) 014-4257',
        '960 Gentle Oak Lane, Shakopee, ND, 58618-6277, US, (701) 327-1219',
        '6918 Cotton Pine Corner, Kenaston, IA, 52165-3975, US, (515) 906-7427',
        '2368 Burning Woods, Ernfold, NY, 11879-9186, US, (646) 819-0355',
        '5646 Quiet Shadow Chase, Tiger Tail, IA, 52283-5537, US, (712) 375-9225',
        '5466 Foggy Mountain Dale, Sweet Home, MT, 59738-0251, US, (406) 881-1706',
        '5313 Clear Willow Route, Amazon, BC, V0S-2S6, CA, (604) 340-7596',
        '7000 Pleasant Autoroute, Spaceport City, UT, 84749-2448, US, (435) 154-3360',
        '8359 Quaking Anchor Road, Gross, BC, V9O-0H5, CA, (250) 985-3859',
        '5143 Amber Deer Hollow, New Deal, ND, 58446-0853, US, (701) 927-0322',
        '6230 Jagged Bear Key, Young, AR, 72337-3811, US, (501) 805-7239',
        '7207 Heather Vista, Devon, WY, 82520-1771, US, (307) 358-7092',
        '9416 Red Rise Place, Spraytown, OK, 73809-4766, US, (580) 867-1973',
        '3770 Golden Horse Diversion, Yelland, IL, 60471-1487, US, (224) 717-9349',
        '4819 Honey Treasure Park, Alaska, NB, E1U-3I0, CA, (506) 656-9138',
        '6187 Round Front, Land O Lakes, AK, 99873-6403, US, (907) 853-9063',
        '9218 Crystal Highway, Pickelville, MT, 59847-9299, US, (406) 076-0024',
        '6737 Bright Quay, Lazy Mountain, KY, 42390-4772, US, (606) 256-7288',
        '237 Merry Campus, Twentysix, SC, 29330-4909, US, (864) 945-0157',
        '446 Fallen Gate Rise, Petrolia, SC, 29959-9527, US, (864) 826-0553',
        '2347 Indian Boulevard, Frisbee, VA, 23797-6458, US, (703) 656-8445',
        '365 Emerald Grove Line, Level, NC, 28381-1514, US, (919) 976-7958',
        '1207 Iron Extension, Klickitat, SC, 29197-8571, US, (803) 535-7888',
        '6770 Cinder Glen, Caronport, OH, 45053-5002, US, (440) 369-4018',
        '7619 Tawny Carrefour, Senlac, NV, 89529-9876, US, (775) 901-6433'];
}

Create skillFilter.ts file
import RefData from './refData';

var SKILL_TEMPLATE =
    '<label style="border: 1px solid lightgrey; margin: 4px; padding: 4px; display: inline-block;">' +
    '  <span>' +
    '    <div style="text-align: center;">SKILL_NAME</div>' +
    '    <div>' +
    '      <input type="checkbox"/>' +
    '      <img src="assets/img/skills/SKILL.png" width="30px"/>' +
    '    </div>' +
    '  </span>' +
    '</label>';

var FILTER_TITLE =
    '<div style="text-align: center; background: lightgray; width: 100%; display: block; border-bottom: 1px solid grey;">' +
    '<b>TITLE_NAME</b>' +
    '</div>';

export default class SkillFilter {

    private filterChangedCallback: Function;
    private model: any;

    private init(params) {
        this.filterChangedCallback = params.filterChangedCallback;
        this.model = {
            android: false,
            css: false,
            html5: false,
            mac: false,
            windows: false
        };
    };

    private getGui() {
        var eGui = document.createElement('div');
        eGui.style.width = '380px';
        var eInstructions = document.createElement('div');
        eInstructions.innerHTML = FILTER_TITLE.replace('TITLE_NAME', 'Custom Skills Filter');
        eGui.appendChild(eInstructions);

        var that = this;

        RefData.IT_SKILLS.forEach(function (skill, index) {
            var skillName = RefData.IT_SKILLS_NAMES[index];
            var eSpan = document.createElement('span');
            var html = SKILL_TEMPLATE.replace("SKILL_NAME", skillName).replace("SKILL", skill);
            eSpan.innerHTML = html;

            var eCheckbox = <HTMLInputElement> eSpan.querySelector('input');
            eCheckbox.addEventListener('click', function () {
                that.model[skill] = eCheckbox.checked;
                that.filterChangedCallback();
            });

            eGui.appendChild(eSpan);
        });

        return eGui;
    };

    private doesFilterPass(params) {

        var rowSkills = params.data.skills;
        var model = this.model;
        var passed = true;

        RefData.IT_SKILLS.forEach(function (skill) {
            if (model[skill]) {
                if (!rowSkills[skill]) {
                    passed = false;
                }
            }
        });

        return passed;
    };

    private isFilterActive() {
        var model = this.model;
        var somethingSelected = model.android || model.css || model.html5 || model.mac || model.windows;
        return somethingSelected;
    };


}

Edit basicTables.html file and add new row with ag-grid
 <div class="row">
    <div class="col-lg-12">
      <ba-card title="Ag Table" baCardClass="with-scroll table-panel">
        <ag-table></ag-table>
      </ba-card>
    </div>    
</div>

Edit basicTables.component.ts file and add new directive
import {Component, ViewEncapsulation} from '@angular/core';

import {BasicTablesService} from './basicTables.service';
import {BaCard} from '../../../../theme/components';
import {HoverTable} from './components/hoverTable';
import {BorderedTable} from './components/borderedTable';
import {CondensedTable} from './components/condensedTable';
import {StripedTable} from './components/stripedTable';
import {ContextualTable} from './components/contextualTable';
import {ResponsiveTable} from './components/responsiveTable';
import {AgTable} from './components/agTable';

@Component({
  selector: 'basic-tables',
  encapsulation: ViewEncapsulation.None,
  directives: [AgTable, BaCard, HoverTable, BorderedTable, CondensedTable, StripedTable, ContextualTable, ResponsiveTable],
  styles: [require('./basicTables.scss')],
  template: require('./basicTables.html'),
  providers: [BasicTablesService]
})
export class BasicTables {

  constructor() {
  }
}

Run npm start open Basic tables menu. Result should look like this


Sources of this example are available on github https://github.com/andrzejszywala/ng2-admin

Sunday, July 3, 2016

Angular 2, Wildfly, Eclipse and VS Code

This blog will show how to create application using Angular 2 and JEE. I decided to use eclipse and vs code because TypeScript support is much better in vs code.

Let's start with the eclipse configuration.

Download eclipse neon.
In workspace preferences check "Refresh using native hooks or pooling"

Open Eclipse Marketplace and find jbosstools

We need only JBoss AS, Wildfly & EAP Server Tools

Create New Maven Project

Add new remote archetype catalog

Select javaee-bce-archetype

Specify Archetype parameters

This archetype will create simple JEE application with sample entity and REST endpoint.

In pom.xml add maven-war-plugin and set path to directory where npm will copy javascript files


In META-INF create file load-script.sql

Add some sample data
Add load script to persistence.xml

Add new WildFly server to eclipse and run the application







Now it's time to configure client part of the application.

Install VS Code and node.js. Go to project folder and run 
Generate angular 2 appliaction
Go to client folder and open vs code

Create file .ember-cli










In .embel-cli set path where npm will copy transpiled JavaScript files.


run npm install command to build application
e:\ws\ng2\ng2-pom\client>npm install

> client@0.0.0 postinstall e:\ws\ng2\ng2-pom\client
> typings install


├── es6-shim (ambient)
├── angular-protractor (ambient dev)
├── jasmine (ambient dev)
└── selenium-webdriver (ambient dev)

balanced-match@0.4.1 node_modules\angular-cli\node_modules\ember-cli\node_modules\tree-sync\node_modules\walk-sync\node_modules\matcher-collection\node_modules\minimatch\node_modules\brace-expansion\node_modules\balanced-match -> node_modules\balanced-match
concat-map@0.0.1 node_modules\angular-cli\node_modules\ember-cli\node_modules\tree-sync\node_modules\walk-sync\node_modules\matcher-collection\node_modules\minimatch\node_modules\brace-expansion\node_modules\concat-map -> node_modules\concat-map
brace-expansion@1.1.5 node_modules\angular-cli\node_modules\ember-cli\node_modules\tree-sync\node_modules\walk-sync\node_modules\matcher-collection\node_modules\minimatch\node_modules\brace-expansion -> node_modules\brace-expansion
minimatch@3.0.2 node_modules\angular-cli\node_modules\ember-cli\node_modules\tree-sync\node_modules\walk-sync\node_modules\matcher-collection\node_modules\minimatch -> node_modules\minimatch
matcher-collection@1.0.2 node_modules\angular-cli\node_modules\ember-cli\node_modules\tree-sync\node_modules\walk-sync\node_modules\matcher-collection -> node_modules\matcher-collection
object-assign@4.1.0 node_modules\angular-cli\node_modules\ember-cli\node_modules\inquirer\node_modules\figures\node_modules\object-assign -> node_modules\object-assign
walk-sync@0.2.6 node_modules\angular-cli\node_modules\ember-cli\node_modules\tree-sync\node_modules\walk-sync -> node_modules\walk-sync
client@0.0.0 e:\ws\ng2\ng2-pom\client
+-- angular-cli@1.0.0-beta.8
| +-- ember-cli@2.5.0
| | +-- exists-sync@0.0.3
| | `-- through@2.3.8
| +-- typescript@1.8.10
| `-- typings@0.8.1
|   +-- any-promise@1.3.0
|   +-- archy@1.0.0
|   +-- bluebird@3.4.1
|   +-- columnify@1.5.4
|   | +-- strip-ansi@3.0.1
|   | | `-- ansi-regex@2.0.0
|   | `-- wcwidth@1.0.1
|   |   `-- defaults@1.0.3
|   |     `-- clone@1.0.2
|   +-- listify@1.0.0
|   +-- minimist@1.2.0
|   +-- typings-core@0.3.1
|   | +-- array-uniq@1.0.3
|   | +-- configstore@2.0.0
|   | | +-- dot-prop@2.4.0
|   | | | `-- is-obj@1.0.1
|   | | +-- os-tmpdir@1.0.1
|   | | +-- osenv@0.1.3
|   | | | `-- os-homedir@1.0.1
|   | | +-- uuid@2.0.2
|   | | +-- write-file-atomic@1.1.4
|   | | | +-- imurmurhash@0.1.4
|   | | | `-- slide@1.1.6
|   | | `-- xdg-basedir@2.0.0
|   | |   `-- os-homedir@1.0.1
|   | +-- debug@2.2.0
|   | | `-- ms@0.7.1
|   | +-- detect-indent@4.0.0
|   | | `-- repeating@2.0.1
|   | |   `-- is-finite@1.0.1
|   | |     `-- number-is-nan@1.0.0
|   | +-- graceful-fs@4.1.4
|   | +-- has@1.0.1
|   | | `-- function-bind@1.1.0
|   | +-- invariant@2.2.1
|   | | `-- loose-envify@1.2.0
|   | |   `-- js-tokens@1.0.3
|   | +-- is-absolute@0.2.5
|   | | +-- is-relative@0.2.1
|   | | | `-- is-unc-path@0.1.1
|   | | |   `-- unc-path-regex@0.1.2
|   | | `-- is-windows@0.1.1
|   | +-- lockfile@1.0.1
|   | +-- make-error-cause@1.1.1
|   | | `-- make-error@1.1.1
|   | +-- mkdirp@0.5.1
|   | | `-- minimist@0.0.8
|   | +-- object.pick@1.1.2
|   | | `-- isobject@2.1.0
|   | |   `-- isarray@1.0.0
|   | +-- parse-json@2.2.0
|   | | `-- error-ex@1.3.0
|   | |   `-- is-arrayish@0.2.1
|   | +-- popsicle@5.0.1
|   | | +-- arrify@1.0.1
|   | | +-- concat-stream@1.5.1
|   | | | +-- inherits@2.0.1
|   | | | +-- readable-stream@2.0.6
|   | | | | +-- core-util-is@1.0.2
|   | | | | +-- isarray@1.0.0
|   | | | | +-- process-nextick-args@1.0.7
|   | | | | +-- string_decoder@0.10.31
|   | | | | `-- util-deprecate@1.0.2
|   | | | `-- typedarray@0.0.6
|   | | +-- form-data@0.2.0
|   | | | +-- async@0.9.2
|   | | | +-- combined-stream@0.0.7
|   | | | | `-- delayed-stream@0.0.5
|   | | | `-- mime-types@2.0.14
|   | | |   `-- mime-db@1.12.0
|   | | +-- methods@1.1.2
|   | | `-- tough-cookie@2.2.2
|   | +-- popsicle-proxy-agent@1.0.0
|   | | +-- http-proxy-agent@1.0.0
|   | | | +-- agent-base@2.0.1
|   | | | | `-- semver@5.0.3
|   | | | `-- extend@3.0.0
|   | | `-- https-proxy-agent@1.0.0
|   | |   +-- agent-base@2.0.1
|   | |   | `-- semver@5.0.3
|   | |   `-- extend@3.0.0
|   | +-- popsicle-retry@2.0.0
|   | +-- popsicle-status@1.0.2
|   | +-- promise-finally@2.2.1
|   | +-- rc@1.1.6
|   | | +-- deep-extend@0.4.1
|   | | +-- ini@1.3.4
|   | | `-- strip-json-comments@1.0.4
|   | +-- rimraf@2.5.2
|   | +-- sort-keys@1.1.2
|   | | `-- is-plain-obj@1.1.0
|   | +-- string-template@1.0.0
|   | +-- strip-bom@2.0.0
|   | | `-- is-utf8@0.2.1
|   | +-- thenify@3.2.0
|   | +-- throat@2.0.2
|   | +-- touch@1.0.0
|   | | `-- nopt@1.0.10
|   | |   `-- abbrev@1.0.9
|   | `-- zip-object@0.1.0
|   +-- update-notifier@0.6.3
|   | +-- boxen@0.3.1
|   | | +-- filled-array@1.1.0
|   | | +-- repeating@2.0.1
|   | | | `-- is-finite@1.0.1
|   | | |   `-- number-is-nan@1.0.0
|   | | +-- string-width@1.0.1
|   | | | +-- code-point-at@1.0.0
|   | | | | `-- number-is-nan@1.0.0
|   | | | +-- is-fullwidth-code-point@1.0.0
|   | | | | `-- number-is-nan@1.0.0
|   | | | `-- strip-ansi@3.0.1
|   | | |   `-- ansi-regex@2.0.0
|   | | `-- widest-line@1.0.0
|   | +-- configstore@2.0.0
|   | | +-- dot-prop@2.4.0
|   | | | `-- is-obj@1.0.1
|   | | +-- graceful-fs@4.1.4
|   | | +-- mkdirp@0.5.1
|   | | | `-- minimist@0.0.8
|   | | +-- os-tmpdir@1.0.1
|   | | +-- osenv@0.1.3
|   | | | `-- os-homedir@1.0.1
|   | | +-- uuid@2.0.2
|   | | +-- write-file-atomic@1.1.4
|   | | | +-- imurmurhash@0.1.4
|   | | | `-- slide@1.1.6
|   | | `-- xdg-basedir@2.0.0
|   | |   `-- os-homedir@1.0.1
|   | +-- is-npm@1.0.0
|   | +-- latest-version@2.0.0
|   | | `-- package-json@2.3.2
|   | |   +-- got@5.6.0
|   | |   | +-- create-error-class@3.0.2
|   | |   | | `-- capture-stack-trace@1.0.0
|   | |   | +-- duplexer2@0.1.4
|   | |   | +-- is-plain-obj@1.1.0
|   | |   | +-- is-redirect@1.0.0
|   | |   | +-- is-retry-allowed@1.0.0
|   | |   | +-- is-stream@1.1.0
|   | |   | +-- lowercase-keys@1.0.0
|   | |   | +-- node-status-codes@1.0.0
|   | |   | +-- parse-json@2.2.0
|   | |   | | `-- error-ex@1.3.0
|   | |   | |   `-- is-arrayish@0.2.1
|   | |   | +-- pinkie-promise@2.0.1
|   | |   | | `-- pinkie@2.0.4
|   | |   | +-- read-all-stream@3.1.0
|   | |   | +-- readable-stream@2.1.4
|   | |   | | +-- buffer-shims@1.0.0
|   | |   | | +-- core-util-is@1.0.2
|   | |   | | +-- inherits@2.0.1
|   | |   | | +-- isarray@1.0.0
|   | |   | | +-- process-nextick-args@1.0.7
|   | |   | | +-- string_decoder@0.10.31
|   | |   | | `-- util-deprecate@1.0.2
|   | |   | +-- timed-out@2.0.0
|   | |   | +-- unzip-response@1.0.0
|   | |   | `-- url-parse-lax@1.0.0
|   | |   |   `-- prepend-http@1.0.4
|   | |   +-- rc@1.1.6
|   | |   | +-- deep-extend@0.4.1
|   | |   | +-- ini@1.3.4
|   | |   | `-- strip-json-comments@1.0.4
|   | |   +-- registry-url@3.1.0
|   | |   `-- semver@5.1.0
|   | `-- semver-diff@2.1.0
|   |   `-- semver@5.1.0
|   +-- wordwrap@1.0.0
|   `-- xtend@4.0.1
`-- protractor@3.3.0
  `-- request@2.67.0
    `-- http-signature@1.1.1
      `-- sshpk@1.8.3
        +-- ecc-jsbn@0.1.1
        +-- jodid25519@1.0.2
        +-- jsbn@0.1.0
        `-- tweetnacl@0.13.3

npm WARN optional Skipping failed optional dependency /karma/chokidar/fsevents:
npm WARN notsup Not compatible with your operating system or architecture: fsevents@1.0.12

Run ng serve command

e:\ws\ng2\ng2-pom\client>ng serve
(node:9580) fs: re-evaluating native module sources is not supported. If you are using the graceful-fs module, please update it to a more recent version.
Livereload server on http://localhost:49152
Serving on http://localhost:4200/


Application is available at url http://localhost:4200/ but not at http://localhost:8080/workshops/index.html so let's fix this. Open index.html file in vs code change base tag and remove ember-cli-live-reload.js

Edit app.component.html file

{{title}}:
<table border="1">
  <tr>
    <th>Id</th>
    <th>Price</th>
  </tr>
  <tr *ngFor="let item of items">
    <td>{{item['confirmation-id']}}</td>
    <td>{{item.price}}</td>
  </tr>
</table>

Edit app.component.ts file
import { Component, OnInit } from '@angular/core';
import { HTTP_PROVIDERS, Response, Http } from '@angular/http';
import { Observable }     from 'rxjs/Observable';
import 'rxjs/add/operator/map';

@Component({
  moduleId: module.id,
  selector: 'app-root',
  templateUrl: 'app.component.html',
  styleUrls: ['app.component.css'],
  providers: [HTTP_PROVIDERS]
})
export class AppComponent implements OnInit {
  title = 'Prices';
  items;

  constructor (private http: Http) {}

  ngOnInit() {
    this.getItems();
  }

  getItems() {
    this.http.get('resources/registrations/')
      .map(response => {
        return response.json();
      })
      .subscribe(items => this.items = items)
      ;
  }

  private extractData(res: Response) {
    let body = res.json();
    return body.data || { };
  }
  private handleError (error: any) {
    // In a real world app, we might use a remote logging infrastructure
    // We'd also dig deeper into the error to get a better message
    let errMsg = (error.message) ? error.message :
      error.status ? `${error.status} - ${error.statusText}` : 'Server error';
    console.error(errMsg); // log to console instead
    return Observable.throw(errMsg);
  }
}

Final result should look like this



Let's configure maven to build angular appliaction
Create pom.xml in client folder
<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>org.jdn</groupId>
 <artifactId>client</artifactId>
 <version>0.0.1-SNAPSHOT</version>
 <packaging>pom</packaging>

 <build>
  <plugins>
   <plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>exec-maven-plugin</artifactId>
    <version>1.1.1</version>
    <executions>
     <execution>
      <id>some-execution</id>
      <phase>compile</phase>
      <goals>
       <goal>exec</goal>
      </goals>
     </execution>
    </executions>
    <configuration>
     <executable>ng</executable>
     <arguments>
      <argument>build</argument>
      <argument>-prod</argument>
     </arguments>
    </configuration>
   </plugin>
  </plugins>
 </build>
</project>

Add new module to main 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>org.jdn</groupId>
    <artifactId>ng2-pom</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>pom</packaging>
    <modules>
     <module>client</module>
        <module>workshops</module>
        <module>workshops-st</module>
    </modules>
</project>

Run maven to build project

e:\ws\ng2\ng2-pom>e:\programowanie\maven\bin\mvn install
[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Build Order:
[INFO]
[INFO] client
[INFO] ng2
[INFO] ng2-st
[INFO] ng2-pom
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building client 0.0.1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
Downloading: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-install-plugin/2.4/maven-install-plugin-2.4.pom
Downloaded: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-install-plugin/2.4/maven-install-plugin-2.4.pom (7 KB at 8.5 KB/sec)
[INFO]
[INFO] --- exec-maven-plugin:1.1.1:exec (some-execution) @ client ---
[INFO] '"ng build -prod"' is not recognized as an internal or external command,
[INFO] operable program or batch file.
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO]
[INFO] client ............................................. FAILURE [  1.696 s]
[INFO] ng2 ................................................ SKIPPED
[INFO] ng2-st ............................................. SKIPPED
[INFO] ng2-pom ............................................ SKIPPED
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.918 s
[INFO] Finished at: 2016-07-03T17:42:10+02:00
[INFO] Final Memory: 7M/104M
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.codehaus.mojo:exec-maven-plugin:1.1.1:exec (some-execution) on project client: Result of cmd.exe /X /C ""ng build -prod"" execution is: '1'. -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoExecutionException

e:\ws\ng2\ng2-pom>e:\programowanie\maven\bin\mvn install
[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Build Order:
[INFO]
[INFO] client
[INFO] ng2
[INFO] ng2-st
[INFO] ng2-pom
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building client 0.0.1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- exec-maven-plugin:1.1.1:exec (some-execution) @ client ---
[INFO] (node:4748) fs: re-evaluating native module sources is not supported. If you are using the graceful-fs module, please update it to a more recent version.
[INFO]
[INFO] Running without elevated rights. Running Ember CLI "as Administrator" increases performance significantly.
[INFO] See ember-cli.com/user-guide/#windows for details.
[INFO]
[INFO] Built project successfully. Stored in "../workshops/src/main/angular".
[INFO] File sizes:
[INFO]  - app.component.css: 0 B
[INFO]  - main.js: 844.99 KB (179.68 KB gzipped)
[INFO]  - system-config.js: 1.66 KB (538 B gzipped)
[INFO]  - es6-shim.js: 131.74 KB (30.4 KB gzipped)
[INFO]  - Reflect.js: 36.91 KB (5.29 KB gzipped)
[INFO]  - system.src.js: 159.57 KB (38.99 KB gzipped)
[INFO]  - zone.js: 52.72 KB (11.37 KB gzipped)
[INFO]
[INFO] --- maven-install-plugin:2.4:install (default-install) @ client ---
Downloading: https://repo.maven.apache.org/maven2/org/codehaus/plexus/plexus-utils/3.0.5/plexus-utils-3.0.5.pom
Downloaded: https://repo.maven.apache.org/maven2/org/codehaus/plexus/plexus-utils/3.0.5/plexus-utils-3.0.5.pom (3 KB at 3.7 KB/sec)
Downloading: https://repo.maven.apache.org/maven2/org/codehaus/plexus/plexus-digest/1.0/plexus-digest-1.0.pom
Downloaded: https://repo.maven.apache.org/maven2/org/codehaus/plexus/plexus-digest/1.0/plexus-digest-1.0.pom (2 KB at 15.0 KB/sec)
Downloading: https://repo.maven.apache.org/maven2/org/codehaus/plexus/plexus-components/1.1.7/plexus-components-1.1.7.pom
Downloaded: https://repo.maven.apache.org/maven2/org/codehaus/plexus/plexus-components/1.1.7/plexus-components-1.1.7.pom (5 KB at 68.4 KB/sec)
Downloading: https://repo.maven.apache.org/maven2/org/codehaus/plexus/plexus/1.0.8/plexus-1.0.8.pom
Downloaded: https://repo.maven.apache.org/maven2/org/codehaus/plexus/plexus/1.0.8/plexus-1.0.8.pom (8 KB at 98.1 KB/sec)
Downloading: https://repo.maven.apache.org/maven2/org/codehaus/plexus/plexus-container-default/1.0-alpha-8/plexus-container-default-1.0-alpha-8.pom
Downloaded: https://repo.maven.apache.org/maven2/org/codehaus/plexus/plexus-container-default/1.0-alpha-8/plexus-container-default-1.0-alpha-8.pom (8 KB at 81.5 KB/sec)
Downloading: https://repo.maven.apache.org/maven2/org/codehaus/plexus/plexus-utils/3.0.5/plexus-utils-3.0.5.jar
Downloading: https://repo.maven.apache.org/maven2/org/codehaus/plexus/plexus-digest/1.0/plexus-digest-1.0.jar
Downloaded: https://repo.maven.apache.org/maven2/org/codehaus/plexus/plexus-digest/1.0/plexus-digest-1.0.jar (12 KB at 84.5 KB/sec)
Downloaded: https://repo.maven.apache.org/maven2/org/codehaus/plexus/plexus-utils/3.0.5/plexus-utils-3.0.5.jar (226 KB at 1000.2 KB/sec)
[INFO] Installing E:\ws\ng2\ng2-pom\client\pom.xml to C:\Users\Andrzej\.m2\repository\org\jdn\client\0.0.1-SNAPSHOT\client-0.0.1-SNAPSHOT.pom
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building ng2 0.0.1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ ng2 ---
[WARNING] Using platform encoding (Cp1250 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 2 resources
[INFO]
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ ng2 ---
[INFO] Changes detected - recompiling the module!
[WARNING] File encoding has not been set, using platform encoding Cp1250, i.e. build is platform dependent!
[INFO] Compiling 6 source files to E:\ws\ng2\ng2-pom\workshops\target\classes
[WARNING] /E:/ws/ng2/ng2-pom/workshops/src/main/java/com/airhacks/workshops/business/registrations/boundary/Registrations.java: E:\ws\ng2\ng2-pom\workshops\src\main\java\com\airhacks\workshops\busines
s\registrations\boundary\Registrations.java uses unchecked or unsafe operations.
[WARNING] /E:/ws/ng2/ng2-pom/workshops/src/main/java/com/airhacks/workshops/business/registrations/boundary/Registrations.java: Recompile with -Xlint:unchecked for details.
[INFO]
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ ng2 ---
[WARNING] Using platform encoding (Cp1250 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 1 resource
[INFO]
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ ng2 ---
[INFO] Changes detected - recompiling the module!
[WARNING] File encoding has not been set, using platform encoding Cp1250, i.e. build is platform dependent!
[INFO] Compiling 6 source files to E:\ws\ng2\ng2-pom\workshops\target\test-classes
[WARNING] /E:/ws/ng2/ng2-pom/workshops/src/test/java/com/airhacks/workshops/business/registrations/entity/RegistrationTest.java: Some input files use unchecked or unsafe operations.
[WARNING] /E:/ws/ng2/ng2-pom/workshops/src/test/java/com/airhacks/workshops/business/registrations/entity/RegistrationTest.java: Recompile with -Xlint:unchecked for details.
[INFO]
[INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ ng2 ---
[INFO] Surefire report directory: E:\ws\ng2\ng2-pom\workshops\target\surefire-reports
Downloading: https://repo.maven.apache.org/maven2/org/apache/maven/surefire/surefire-junit4/2.12.4/surefire-junit4-2.12.4.pom
Downloaded: https://repo.maven.apache.org/maven2/org/apache/maven/surefire/surefire-junit4/2.12.4/surefire-junit4-2.12.4.pom (3 KB at 28.7 KB/sec)
Downloading: https://repo.maven.apache.org/maven2/org/apache/maven/surefire/surefire-providers/2.12.4/surefire-providers-2.12.4.pom
Downloaded: https://repo.maven.apache.org/maven2/org/apache/maven/surefire/surefire-providers/2.12.4/surefire-providers-2.12.4.pom (3 KB at 31.4 KB/sec)
Downloading: https://repo.maven.apache.org/maven2/org/apache/maven/surefire/surefire-junit4/2.12.4/surefire-junit4-2.12.4.jar
Downloaded: https://repo.maven.apache.org/maven2/org/apache/maven/surefire/surefire-junit4/2.12.4/surefire-junit4-2.12.4.jar (37 KB at 424.2 KB/sec)

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running com.airhacks.workshops.business.registrations.boundary.RegistrationsTest
Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.577 sec
Running com.airhacks.workshops.business.registrations.control.VatCalculatorTest
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.002 sec
Running com.airhacks.workshops.business.registrations.entity.RegistrationTest
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.004 sec

Results :

Tests run: 7, Failures: 0, Errors: 0, Skipped: 0

[INFO]
[INFO] --- maven-war-plugin:2.6:war (default-war) @ ng2 ---
[INFO] Packaging webapp
[INFO] Assembling webapp [ng2] in [E:\ws\ng2\ng2-pom\workshops\target\workshops]
[INFO] Processing war project
[INFO] Copying webapp webResources [E:\ws\ng2\ng2-pom\workshops\src/main/angular] to [E:\ws\ng2\ng2-pom\workshops\target\workshops]
[INFO] Copying webapp resources [E:\ws\ng2\ng2-pom\workshops\src\main\webapp]
[INFO] Webapp assembled in [147 msecs]
[INFO] Building war: E:\ws\ng2\ng2-pom\workshops\target\workshops.war
[INFO]
[INFO] --- maven-install-plugin:2.4:install (default-install) @ ng2 ---
[INFO] Installing E:\ws\ng2\ng2-pom\workshops\target\workshops.war to C:\Users\Andrzej\.m2\repository\org\jdn\ng2\0.0.1-SNAPSHOT\ng2-0.0.1-SNAPSHOT.war
[INFO] Installing E:\ws\ng2\ng2-pom\workshops\pom.xml to C:\Users\Andrzej\.m2\repository\org\jdn\ng2\0.0.1-SNAPSHOT\ng2-0.0.1-SNAPSHOT.pom
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building ng2-st 0.0.1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ ng2-st ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory E:\ws\ng2\ng2-pom\workshops-st\src\main\resources
[INFO]
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ ng2-st ---
[INFO] No sources to compile
[INFO]
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ ng2-st ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory E:\ws\ng2\ng2-pom\workshops-st\src\test\resources
[INFO]
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ ng2-st ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 2 source files to E:\ws\ng2\ng2-pom\workshops-st\target\test-classes
[INFO]
[INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ ng2-st ---
[INFO]
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ ng2-st ---
[INFO] Building jar: E:\ws\ng2\ng2-pom\workshops-st\target\ng2-st-0.0.1-SNAPSHOT.jar
[INFO]
[INFO] --- maven-install-plugin:2.4:install (default-install) @ ng2-st ---
[INFO] Installing E:\ws\ng2\ng2-pom\workshops-st\target\ng2-st-0.0.1-SNAPSHOT.jar to C:\Users\Andrzej\.m2\repository\org\jdn\ng2-st\0.0.1-SNAPSHOT\ng2-st-0.0.1-SNAPSHOT.jar
[INFO] Installing E:\ws\ng2\ng2-pom\workshops-st\pom.xml to C:\Users\Andrzej\.m2\repository\org\jdn\ng2-st\0.0.1-SNAPSHOT\ng2-st-0.0.1-SNAPSHOT.pom
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building ng2-pom 0.0.1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-install-plugin:2.4:install (default-install) @ ng2-pom ---
[INFO] Installing e:\ws\ng2\ng2-pom\pom.xml to C:\Users\Andrzej\.m2\repository\org\jdn\ng2-pom\0.0.1-SNAPSHOT\ng2-pom-0.0.1-SNAPSHOT.pom
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO]
[INFO] client ............................................. SUCCESS [ 27.767 s]
[INFO] ng2 ................................................ SUCCESS [  6.487 s]
[INFO] ng2-st ............................................. SUCCESS [  0.911 s]
[INFO] ng2-pom ............................................ SUCCESS [  0.050 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 35.345 s
[INFO] Finished at: 2016-07-03T17:44:25+02:00
[INFO] Final Memory: 29M/163M
[INFO] ------------------------------------------------------------------------