domingo, 9 de julio de 2017

Nuevo Esquema Offline de Documentos Electrónicos | SRI

Desde la publicación de la Resolución NAC-DGERCGC14-00790, el SRI puso en marcha un nuevo esquema para la emisión de comprobantes electrónicos: el esquema Off-line. Actualmente este esquema, con el propósito de que los emisores de comprobantes que lo deseen puedan realizar pruebas y ajustar sus servicios a este nuevo esquema, está disponible en las siguientes URL's:

Uno es para el ambiente de Pruebas en donde cada contribuyente certificará que su aplicación funcione correctamente con cada tipo de comprobante electrónico.
https://celcer.sri.gob.ec/comprobantes-electronicos-ws/RecepcionComprobantesOffline?wsdl
https://celcer.sri.gob.ec/comprobantes-electronicos-ws/AutorizacionComprobantesOffline?wsdl

El segundo es para el ambiente de Producción, al cual cada contribuyente deberá acceder una vez que ha realizado las pruebas y esté seguro que su aplicación funciona correctamente, las direcciones del los WS son las siguientes:
https://cel.sri.gob.ec/comprobantes-electronicos-ws/RecepcionComprobantesOffline?wsdl
https://cel.sri.gob.ec/comprobantes-electronicos-ws/AutorizacionComprobantesOffline?wsdl

Con el nuevo esquema Off-line, se puede generar un comprobante en tres sencillos pasos; ya que el cambio más significativo se encuentra en que los comprobantes serán enviados al mismo tiempo tanto al receptor como al SRI. Es decir, el mismo comprobante que recibe el SRI para su validación es el que recibirán sus socios de negocio.

Figura 1. Diferencias entre el esquema ONLINE y el nuevo esquema OFFLINE (Fuente: SRI)

En RP Consulting ya disponemos del nuevo esquema Off-line. Más de 20 clientes en Ecuador confirman la calidad de nuestro servicio de consultoría y capacitación en temas de facturación electrónica. 


Te invitamos a ponerte en contacto con nosotros en el siguiente formulario para evaluar la integración de este nuevo esquema, ya sea a través de librerías, dll, apis o servicios Web. Contamos con los consultores más especializados en el tema.

lunes, 1 de mayo de 2017

HTML y Latex: MathJax

MathJax es un motor de visualización JavaScript de código abierto para la notación LaTeX, MathML y ​​AsciiMath que funciona en todos los navegadores modernos. Su uso es tan sencillo como el código que muestro a continuación, sin embargo los resultados son asombrosos.
<!DOCTYPE html>
<html>
   <head>
      <title>MathJax TeX Test Page</title>
      <!-- Copyright (c) 2010-2017 The MathJax Consortium -->
      <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
      <meta http-equiv="X-UA-Compatible" content="IE=edge" />
      <meta name="viewport" content="width=device-width, initial-scale=1">
      <script type="text/x-mathjax-config">
         MathJax.Hub.Config({
           tex2jax: {inlineMath: [["$","$"],["\(","\)"]]}
         });
      </script>
      <script type="text/javascript" async
         src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-MML-AM_CHTML"></script>
   </head>
   <body>
      <h1>Matemáticas con MathJax</h1>
      <p>
         Cuando $a ne 0$, existen dos soluciones para (ax^2 + bx + c = 0) y son
         $$x = {-b pm sqrt{b^2-4ac} over 2a}.$$
      </p>
      <h2>La Ecuaci&oacute;n de Lorenz</h2>
      <p>
         begin{align}
         dot{x} & = sigma(y-x) \
         dot{y} & = rho x - y - xz \
         dot{z} & = -beta z + xy
         end{align}
      </p>
      <h2>La Desigualdad de Cauchy-Schwarz</h2>
      <p>[
         left( sum_{k=1}^n a_k b_k right)^{!!2} leq
         left( sum_{k=1}^n a_k^2 right) left( sum_{k=1}^n b_k^2 right)
         ]
      </p>
      <h2>Producto Vectorial</h2>
      <p>[
         mathbf{V}_1 times mathbf{V}_2 =
         begin{vmatrix}
         mathbf{i} & mathbf{j} & mathbf{k} \
         frac{partial X}{partial u} & frac{partial Y}{partial u} & 0 \
         frac{partial X}{partial v} & frac{partial Y}{partial v} & 0 \
         end{vmatrix}
         ]
      </p>
      <h2>Probabilidad de obtener (k) caras al lanzar (n) monedas es:</h2>
      <p>[P(E) = {n choose k} p^k (1-p)^{ n-k} ]</p>
      <h2>Identidad de Ramanujan</h2>
      <p>[
         frac{1}{(sqrt{phi sqrt{5}}-phi) e^{frac25 pi}} =
         1+frac{e^{-2pi}} {1+frac{e^{-4pi}} {1+frac{e^{-6pi}}
         {1+frac{e^{-8pi}} {1+ldots} } } }
         ]
      </p>
      <h2>Identidad de Rogers-Ramanujan</h2>
      <p>[
         1 +  frac{q^2}{(1-q)}+frac{q^6}{(1-q)(1-q^2)}+cdots =
         prod_{j=0}^{infty}frac{1}{(1-q^{5j+2})(1-q^{5j+3})},
         quadquad text{for $|q|<1$}.
         ]
      </p>
      <h2>Ecuación de Maxwell's</h2>
      <p>
         begin{align}
         nabla times vec{mathbf{B}} -, frac1c, frac{partialvec{mathbf{E}}}{partial t} & = frac{4pi}{c}vec{mathbf{j}} \
         nabla cdot vec{mathbf{E}} & = 4 pi rho \
         nabla times vec{mathbf{E}}, +, frac1c, frac{partialvec{mathbf{B}}}{partial t} & = vec{mathbf{0}} \
         nabla cdot vec{mathbf{B}} & = 0
         end{align}
      </p>
      <h2>Expresiones matem&aacute;ticas dentro de texto</h2>
      <p>Finalmente, mientras que la visualización de ecuaciones se ve bien para una página de muestras, la capacidad de mezclar matemáticas y texto en un párrafo también es importante. Esta expresión (sqrt{3x-1}+(1+x)^2) es un ejemplo de una ecuación en línea.  Las ecuaciones de MathJax pueden usarse de esta manera también, sin perturbar indebidamente el espaciado entre líneas.</p>
   </body>
</html>
Los resultados del código anterior:

Un claro ejemplo donde podemos visualizar MathJax en todo su potencial es en math.stackexchange.com. Les invito a revisar el proyecto que definitivamente es de mucha ayuda a la publicación de resultados para la gente inmersa en la ciencia.

domingo, 30 de abril de 2017

Raphael JS Pie Chart and Spring MVC

Raphael JS is a lightweight JavaScript framework which allows you to draw vector graphics in a browser. In this post, I will introduce you a basic example in order to explain how to create a Pie Chart from a given set of data which will be provided by a Spring Controller and a Ajax calling.

Required Software to Run Example:
  • Java 7
  • Eclipse Mars
  • Apache Tomcat 8.0
  • Maven
  • Raphael JS
Project Structure in Netbeans

POM.xml
<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/maven-v4_0_0.xsd">
 <modelVersion>4.0.0</modelVersion>
 <groupId>com.rolandopalermo.web</groupId>
 <artifactId>spring-raphael</artifactId>
 <packaging>war</packaging>
 <version>0.0.1-SNAPSHOT</version>
 <name>spring-raphael Maven Webapp</name>
 <url>http://maven.apache.org</url>
 <dependencies>
  <dependency>
   <groupId>javax.servlet</groupId>
   <artifactId>jstl</artifactId>
   <version>1.2</version>
  </dependency>
  <dependency>
   <groupId>junit</groupId>
   <artifactId>junit</artifactId>
   <version>3.8.1</version>
   <scope>test</scope>
  </dependency>
  <!-- Spring -->
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-core</artifactId>
   <version>4.3.5.RELEASE</version>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-webmvc</artifactId>
   <version>4.3.5.RELEASE</version>
  </dependency>
  <!-- JSON -->
  <dependency>
   <groupId>com.fasterxml.jackson.core</groupId>
   <artifactId>jackson-core</artifactId>
   <version>2.7.3</version>
  </dependency>
  <dependency>
   <groupId>com.fasterxml.jackson.core</groupId>
   <artifactId>jackson-databind</artifactId>
   <version>2.7.3</version>
  </dependency>
 </dependencies>
 <build>
  <finalName>spring-raphael</finalName>
 </build>
</project>
Pie.js
Raphael.fn.pieChart = function(cx, cy, r, values, labels, stroke) {
 var paper = this;
 var rad = Math.PI / 180;// degrees to radians
 var chart = this.set();
 var angle = 0, total = 0, start = 0;
 // Draw a sector
 function sector(cx, cy, r, startAngle, endAngle, params) {
  console.log(params.fill);
  var x1 = cx + r * Math.cos(-startAngle * rad), x2 = cx + r
    * Math.cos(-endAngle * rad), y1 = cy + r
    * Math.sin(-startAngle * rad), y2 = cy + r
    * Math.sin(-endAngle * rad);
  return paper.path(
    [ "M", cx, cy, "L", x1, y1, "A", r, r, 0,
      +(endAngle - startAngle > 180), 0, x2, y2, "z" ]).attr(
    params);
 }

 process = function(j) {
  var value = values[j], angleplus = 360 * value / total, popangle = angle
    + (angleplus / 2), color = Raphael.hsb(start, .75, 1), ms = 500, delta = 30, bcolor = Raphael
    .hsb(start, 1, 1), p = sector(cx, cy, r, angle, angle
    + angleplus, {
   fill : "90-" + bcolor + "-" + color,
   stroke : stroke,
   "stroke-width" : 3
  }), txt = paper.text(cx + (r + delta + 55) * Math.cos(-popangle * rad),
    cy + (r + delta + 25) * Math.sin(-popangle * rad), labels[j])
    .attr({
     fill : bcolor,
     stroke : "none",
     opacity : 0,
     "font-size" : 20
    });
  p.mouseover(function() {
   p.stop().animate({
    transform : "s1.1 1.1 " + cx + " " + cy
   }, ms, "elastic");
   txt.stop().animate({
    opacity : 1
   }, ms, "elastic");
  }).mouseout(function() {
   p.stop().animate({
    transform : ""
   }, ms, "elastic");
   txt.stop().animate({
    opacity : 0
   }, ms);
  });
  angle += angleplus;
  chart.push(p);
  chart.push(txt);
  start += .1;
 };
 for (var i = 0, ii = values.length; i < ii; i++) {
  total += values[i];
 }
 for (i = 0; i < ii; i++) {
  process(i);
 }
 return chart;
};

$(function() {
 $.ajax({
  method: "POST",
        url: 'get-full-report',
        dataType: 'json',
        crossDomain: true,
        beforeSend: function (xhr) {
            //Set authentication headers
        },
        success: function (data, textStatus, jqXHR) {
         if (data.error) {
                alert(data.message);
            } else {
             var values = [], labels = [];
             $.each(data, function(i, item) {
              values.push(parseInt(data[i].percentageOfUse, 10));
              labels.push(data[i].name);
             });
             Raphael("holder", 700, 700).pieChart(350, 350, 200, values, labels, "#fff");
            }
        },
        error: function (xhr) { // if error occured
            alert('No se pudo procesar la solicitud.');
        },
        complete: function () {
        },
        xhrFields: {
            withCredentials: true
        }
 });
});
Spring Controller
package com.rolandopalermo.web.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import com.rolandopalermo.web.bean.Language;
import com.rolandopalermo.web.util.ReportUtil;
import com.rolandopalermo.web.util.ResponseList;

@Controller
public class HomeController {

 @RequestMapping(value = { "/", "/home" }, method = RequestMethod.GET)
 public ModelAndView home() {
  ModelAndView model = new ModelAndView();
  model.setViewName("index");
  return model;
 }

 @RequestMapping(value = { "get-full-report" }, method = RequestMethod.POST)
 @CrossOrigin(origins = "*", allowCredentials = "true")
 @ResponseBody
 public String getReport() {
  String jsonResponse = "";
  ResponseList<Language> list = ReportUtil.generateReport();
  if (list == null || list.isEmpty()) {
   jsonResponse = "{"error" : true, "message": "No available data"}";
  } else {
   jsonResponse = list.toString();
  }
  return jsonResponse;
 }

}

And your output should look like:


Download the full source code from Github and enjoy it!

lunes, 24 de abril de 2017

How to use Jquery datatable in Spring MVC

Creating a table data along with features like pagination, filtering the records, sorting, no of records to show etc. is a challenger job for developers because it involves lot of designs and coding. But now it becomes very easy to develop with the help of jQuery datatable plugin. In this post we will show how to use jQuery datatable in Spring MVC 4 application.

Required Software to Run Example:
  • Java 7
  • Netbeans
  • Maven
  • MySQL
Project Structure in Netbeans


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>com.rolandopalermo.web</groupId>
    <artifactId>spring-datatable</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <name>spring-datatable</name>

    <properties>
        <endorsed.dir>${project.build.directory}/endorsed</endorsed.dir>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    
    <dependencies>
        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-web-api</artifactId>
            <version>7.0</version>
            <scope>provided</scope>
        </dependency>
        
        <!-- Spring -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>4.3.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>4.3.5.RELEASE</version>
        </dependency>
        
        <!-- Jackson -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.7.3</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.7.3</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.7</source>
                    <target>1.7</target>
                    <compilerArguments>
                        <endorseddirs>${endorsed.dir}</endorseddirs>
                    </compilerArguments>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>2.3</version>
                <configuration>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <version>2.6</version>
                <executions>
                    <execution>
                        <phase>validate</phase>
                        <goals>
                            <goal>copy</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>${endorsed.dir}</outputDirectory>
                            <silent>true</silent>
                            <artifactItems>
                                <artifactItem>
                                    <groupId>javax</groupId>
                                    <artifactId>javaee-endorsed-api</artifactId>
                                    <version>7.0</version>
                                    <type>jar</type>
                                </artifactItem>
                            </artifactItems>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>
POJO Class used in Example
package com.rolandopalermo.domain;

import com.rolandopalermo.util.EmployeeStatus;
import com.rolandopalermo.util.JsonConverter;
import java.io.Serializable;

/**
 *
 * @author rolan
 */
public class Employee implements Serializable {

    private int id;
    private String name;
    private String surname;
    private int age;
    private String address;
    private EmployeeStatus status;

    public Employee(int id, String name, String surname, int age, String address, EmployeeStatus status) {
        this.id = id;
        this.name = name;
        this.surname = surname;
        this.age = age;
        this.address = address;
        this.status = status;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSurname() {
        return surname;
    }

    public void setSurname(String surname) {
        this.surname = surname;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
    
    public EmployeeStatus getStatus() {
        return status;
    }

    public void setStatus(EmployeeStatus status) {
        this.status = status;
    }

    @Override
    public String toString() {
        String json = JsonConverter.INSTANCE.asJsonString(this);
        return json;
    }

}
Spring MVC Controller
package com.rolandopalermo.controller;

import com.rolandopalermo.domain.Employee;
import com.rolandopalermo.test.DummyDAO;
import com.rolandopalermo.util.ResponseList;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

/**
 *
 * @author rolan
 */
@Controller
public class HomeController {

    @RequestMapping(value = {"/", "/home"}, method = RequestMethod.GET)
    public ModelAndView home() {
        ModelAndView model = new ModelAndView();
        model.setViewName("home");
        return model;
    }

    @RequestMapping(value = {"/get-all-employees"}, method = RequestMethod.POST)
    @CrossOrigin(origins = "*", allowCredentials = "true")
    @ResponseBody
    public String getAllEmployees(HttpServletRequest request, HttpServletResponse response) {
        String json;
        try {
            ResponseList<Employee> lstInvoices = DummyDAO.getAllEmployees();
            json = lstInvoices.toString();
        } catch (Exception e) {
            json = "{"error" : true, "message": "" + e.getMessage() + "."}";
        }
        return json;
    }
}
Javascript
$(document).ready(function () {
    $.ajax({
        method: "POST",
        url: 'get-all-employees',
        dataType: 'json',
        crossDomain: true,
        beforeSend: function (xhr) {
            //Set authentication headers
        },
        success: function (data, textStatus, jqXHR) {
            if (data.error) {
                alert(data.message);
            } else {
                var selector = $("#applications");
                selector.html("");
                selector.append(JSON.stringify(data));
                data = $.parseJSON(selector.text());
                console.log(data);
                table = $("#employees").DataTable({
                    data: data,
                    scrollX: true,
                    scrollCollapse: true,
                    fixedColumns: true,
                    columns: [{
                            data: "id",
                            visible: false
                        }, {
                            data: "name"
                        }, {
                            data: "surname"
                        }, {
                            data: "age"
                        }, {
                            data: "address"
                        }, {
                            data: "status",
                            render: function (value) {
                                switch (value) {
                                    case "ACTIVE":
                                        return '<span class=active></span>';
                                    case "PENDING":
                                        return '<span class=pending></span>';
                                    case "INACTIVE":
                                        return '<span class=inactive></span>';
                                    case "DELETED":
                                        return '<span class=deleted></span>';
                                    default:
                                        return '<span class=active></span>';
                                }
                            }
                        }
                    ]
                });
            }
        },
        error: function (xhr) { // if error occured
            alert('No se pudo procesar la solicitud.');
        },
        complete: function () {
        },
        xhrFields: {
            withCredentials: true
        }
    });
});
WebAppInitializer
<%-- 
    Document   : view
    Created on : Jan 20, 2017, 7:15:31 PM
    Author     : Rolando
--%>

<%@ page language="java" pageEncoding="UTF-8" contentType="text/html;charset=UTF-8" %>
<%@taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<script type="text/javascript" src="<c:url value='/static/app/third-party/js/jquery-1.12.4.js' />"></script>
<script type="text/javascript" src="<c:url value='/static/app/third-party/datatables/js/jquery.dataTables.min.js' />"></script>
<script type="text/javascript" src="<c:url value='/static/app/third-party/datatables/js/dataTables.buttons.min.js' />"></script>
<script type="text/javascript" src="<c:url value='/static/app/js/home.js' />"></script>
<link href="<c:url value='/static/app/third-party/datatables/css/jquery.dataTables.min.css'/>" rel="stylesheet"/>
<link href="<c:url value='/static/app/third-party/datatables/css/buttons.dataTables.min.css'/>" rel="stylesheet"/>
<link href="<c:url value='/static/app/css/master.css'/>" rel="stylesheet"/>

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
        <title>How to use Jquery datatable in Spring MVC</title>
    </head>
    <body>
        <div id="applications" style="display: none"></div>
        <table id="employees" cellspacing="0" width="100%">
            <thead>
                <tr>
                    <th>ID</th>
                    <th>Name</th>
                    <th>Surname</th>
                    <th>Age</th>
                    <th>Address</th>
                    <th>Status</th>
                </tr>
            </thead>
            <tbody>
            </tbody>
        </table>
    </body>
</html>
And your output should look like:


Download the full source code from Github and enjoy it!

domingo, 23 de abril de 2017

Spring 4 and MyBatis Java Full Annotations

In this post, we will provide MyBatis 3 annotations example with a simple application example. These annotations are declared in interface on methods for select, insert, update and delete operation. Now this interface will act as Mapper for SQL queries. We need to register mapper interface in MyBatis configuration XML. To map POJO and table columns, MyBatis provides Results annotation which works same as Resultmap tag in XML mapper. @Results helps to get query result by @Select annotation.

Required Software to Run Example:
  • Java 7
  • Netbeans
  • Maven
  • MySQL
Project Structure in Netbeans


Table Schema
CREATE TABLE `mybatis-spring`.`product` (
  `id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(45) NOT NULL,
  `price` DOUBLE NOT NULL,
  `description` TEXT NOT NULL,
  PRIMARY KEY (`id`)
)
ENGINE = InnoDB;
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>com.rolandopalermo.web</groupId>
    <artifactId>spring-mybatis</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <name>spring-mybatis</name>

    <properties>
        <endorsed.dir>${project.build.directory}/endorsed</endorsed.dir>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <mysql.connector.version>5.1.31</mysql.connector.version>
    </properties>
    
    <dependencies>
        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-web-api</artifactId>
            <version>7.0</version>
            <scope>provided</scope>
        </dependency>
        
        <!-- Spring -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>4.3.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>4.3.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>4.2.1.RELEASE</version>
            <type>jar</type>
            <scope>compile</scope>
        </dependency>
        
        <!-- Mybatis -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.3.0</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>1.2.2</version>
        </dependency>
        
        <!-- MySQL -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql.connector.version}</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.7</source>
                    <target>1.7</target>
                    <compilerArguments>
                        <endorseddirs>${endorsed.dir}</endorseddirs>
                    </compilerArguments>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>2.3</version>
                <configuration>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <version>2.6</version>
                <executions>
                    <execution>
                        <phase>validate</phase>
                        <goals>
                            <goal>copy</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>${endorsed.dir}</outputDirectory>
                            <silent>true</silent>
                            <artifactItems>
                                <artifactItem>
                                    <groupId>javax</groupId>
                                    <artifactId>javaee-endorsed-api</artifactId>
                                    <version>7.0</version>
                                    <type>jar</type>
                                </artifactItem>
                            </artifactItems>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>
POJO Class used in Example
package com.rolandopalermo.web.domain;

/**
 *
 * @author rolan
 */
public class Product {

    private long id;
    private String name;
    private double price;
    private String description;

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

}
Mapper Interface Using MyBatis Annotation
package com.rolandopalermo.web.mapper;

import com.rolandopalermo.web.domain.Product;
import java.util.List;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;

/**
 *
 * @author rolan
 */
public interface ProductMapper {

    @Results({
        @Result(property = "id", column = "id"),
        @Result(property = "name", column = "name"),
        @Result(property = "price", column = "price"),
        @Result(property = "description", column = "description")
    })
    @Select("SELECT id, name, price, description from product WHERE id = #{id}")
    Product selectProduct(int id);

    @Insert("INSERT into product(name, price, description) VALUES(#{name}, #{price}, #{description})")
    void insertProduct(Product product);

    @Update("UPDATE product SET name=#{name}, price =#{price}, description =#{description} WHERE id =#{id}")
    void updateProduct(Product product);

    @Delete("DELETE FROM product WHERE id =#{id}")
    void deleteProduct(int id);
    
    @Results({
        @Result(property = "id", column = "id"),
        @Result(property = "name", column = "name"),
        @Result(property = "price", column = "price"),
        @Result(property = "description", column = "description")
    })
    @Select("SELECT id, name, price, description from product")
    List<Product> selectAllProduct();

}
DataConfig
import javax.sql.DataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
//import org.apache.commons.dbcp.BasicDataSource;
import org.mybatis.spring.annotation.MapperScan;
import org.mybatis.spring.transaction.SpringManagedTransactionFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.DriverManagerDataSource;

/**
 *
 * @author Rolando
 */
@Configuration
@PropertySource("classpath:data.properties")
@MapperScan(basePackages = {"com.rolandopalermo.web.mapper"}, sqlSessionFactoryRef = "sqlSessionFactory")
public class DataConfig {

    @Value("${mysql.driver}")
    private String driver;

    @Value("${mysql.url}")
    private String url;

    @Value("${mysql.user}")
    private String username;

    @Value("${mysql.password}")
    private String password;

    @Bean
    public DataSource dataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(driver);
        dataSource.setUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        return dataSource;
    }

    @Bean
    public DataSourceTransactionManager transactionManager() {
        return new DataSourceTransactionManager(dataSource());
    }

    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        sqlSessionFactoryBean.setTransactionFactory(new SpringManagedTransactionFactory());
        return sqlSessionFactoryBean.getObject();
    }

}
MvcConfig
package com.rolandopalermo.web.config;

import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;

/**
 *
 * @author Rolando
 */
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = {"com.rolandopalermo.web"})
public class MvcConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
    }

    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }

    @Bean
    public InternalResourceViewResolver jspViewResolver() {
        InternalResourceViewResolver bean = new InternalResourceViewResolver();
        bean.setPrefix("/WEB-INF/jsp/");
        bean.setSuffix(".jsp");
        return bean;
    }

    @Bean
    public MessageSource messageSource() {
        ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
        messageSource.setBasename("classpath:messages");
        messageSource.setDefaultEncoding("UTF-8");
        return messageSource;
    }

}
WebAppInitializer
package com.rolandopalermo.web.config;

import javax.servlet.ServletContext;
import javax.servlet.ServletRegistration;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;

/**
 *
 * @author Rolando
 */
public class WebAppInitializer implements WebApplicationInitializer {

    @Override
    public void onStartup(ServletContext container) {
        // Create the 'root' Spring application context
        AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
//        rootContext.register(ServiceConfig.class, JPAConfig.class, SecurityConfig.class);
        rootContext.register(MvcConfig.class, DataConfig.class);
        // Manage the lifecycle of the root application context
        container.addListener(new ContextLoaderListener(rootContext));
        // Create the dispatcher servlet's Spring application context
        AnnotationConfigWebApplicationContext dispatcherServlet = new AnnotationConfigWebApplicationContext();
        dispatcherServlet.register(MvcConfig.class);
        // Register and map the dispatcher servlet
        ServletRegistration.Dynamic dispatcher = container.addServlet("dispatcher", new DispatcherServlet(dispatcherServlet));
        dispatcher.setLoadOnStartup(1);
        dispatcher.addMapping("/");
    }

}
Home.jsp
Now we will test our application.
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>JSP Page</title>
    </head>
    <body>
        <h1>Products</h1>
        <c:forEach var="item" items="${products}">
            <c:out value="${item.name} ${item.price}"/>
            <br>
        </c:forEach>
    </body>
</html>
And your output should look like:


Download the full source code from Github and enjoy it!

miércoles, 26 de octubre de 2016

Integración Java+Azure utilizando REST

Cuando escuchamos de Azure, inmediatamente se nos viene a la mente el SDK de esta interesante plataforma de Microsoft, sin embargo algunos desarrolladores preferimos invocar a los servicios de Azure directamente desde  su API REST, sin utilizar bibliotecas específicas. En este artículo encontrarás un ejemplo completo de cómo comunicarnos con Azure Blob Storage.


A continuación, se muestra la clase java que se encarga de realizar la comunicación:
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package util;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.TimeZone;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;

/**
 *
 * @author Rolando
 */
public class Util {

    private static Base64 base64 = new Base64();

    public static void signRequestSK(HttpURLConnection request, String account, String key) throws Exception {
        SimpleDateFormat fmt = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss");
        fmt.setTimeZone(TimeZone.getTimeZone("GMT"));
        String date = fmt.format(Calendar.getInstance().getTime()) + " GMT";

        StringBuilder sb = new StringBuilder();
        sb.append("GET\n"); // method
        sb.append("\n"); // content encoding
        sb.append("\n"); // content language
        sb.append("\n"); // content length
        sb.append("\n"); // md5 (optional)
        sb.append("\n"); // content type
        sb.append("\n"); // legacy date
        sb.append("\n"); // if-modified-since
        sb.append("\n"); // if-match
        sb.append("\n"); // if-none-match
        sb.append("\n"); // if-unmodified-since
        sb.append("\n"); // range
        sb.append("x-ms-date:" + date + "\n"); // headers
        sb.append("x-ms-version:2015-12-11\n");
        sb.append("/" + account + request.getURL().getPath() + "\ncomp:list");
        Mac mac = Mac.getInstance("HmacSHA256");
        mac.init(new SecretKeySpec(base64.decode(key), "HmacSHA256"));
        String authKey = new String(base64.encode(mac.doFinal(sb.toString().getBytes("UTF-8"))));
        String auth = "SharedKey " + account + ":" + authKey;
        request.setRequestProperty("x-ms-date", date);
        request.setRequestProperty("x-ms-version", "2015-12-11");
        request.setRequestProperty("Authorization", auth);
        request.setRequestProperty("Accept-Charset", "UTF-8");
        request.setRequestProperty("Accept", "application/atom+xml,application/xml");
        request.setRequestMethod("GET");
    }

    public static void main(String args[]) throws Exception {
        //To use fiddler
        System.setProperty("http.proxyHost", "127.0.0.1");
        System.setProperty("https.proxyHost", "127.0.0.1");
        System.setProperty("http.proxyPort", "8888");
        System.setProperty("https.proxyPort", "8888");
        //--
        //Inputs
        String account = "<Nombre_Cuenta>";
        String key = "<Access_Key>";
        //--
        HttpURLConnection connection = (HttpURLConnection) (new URL("http://" + account + ".blob.core.windows.net/?comp=list")).openConnection();
        signRequestSK(connection, account, key);
        connection.connect();
        StringBuffer text = new StringBuffer();
        InputStreamReader in = new InputStreamReader((InputStream) connection.getContent());
        BufferedReader buff = new BufferedReader(in);
        String line;
        do {
            line = buff.readLine();
            text.append(line + "\n");
        } while (line != null);
        System.out.println(text);
    }
}
Entonces, si utilizamos Fiddler, podremos ver que este sería el Request:
GET http://"<Nombre_Cuenta>".blob.core.windows.net/?comp=list HTTP/1.1
x-ms-date: Wed, 26 Oct 2016 18:35:49 GMT
x-ms-version: 2015-12-11
Authorization: SharedKey "<Nombre_Cuenta>":psyT2FlwpU3BU7/kKqtfi+ZBTuUpEX8bnsZ45O/1rUk=
Accept-Charset: UTF-8
Accept: application/atom+xml,application/xml
User-Agent: Java/1.8.0_92
Host: "<Nombre_Cuenta>".blob.core.windows.net
Connection: keep-alive
Y el response quedaría de la siguiente manera:
<?xml version="1.0" encoding="UTF-8" ?>
<EnumerationResults ServiceEndpoint="http://"<Nombre_Cuenta>".blob.core.windows.net/">
    <Containers>
        <Container>
            <Name>sas</Name>
            <Properties>
                <Last-Modified>Tue, 25 Oct 2016 19:19:46 GMT</Last-Modified>
                <Etag>"0x8D3FD0BE1F7E8D0"</Etag>
                <LeaseStatus>unlocked</LeaseStatus>
                <LeaseState>available</LeaseState>
            </Properties>
        </Container>
    </Containers>
    <NextMarker />
</EnumerationResults>

viernes, 21 de octubre de 2016

Servicios Web REST y PHP

En este artículo les mostraré cómo implementar una aplicación CRUD con PHP y servicios Web RESTFul. Los requisitos son los siguientes:
  • PHP 4.0 o superior
  • MySQL 4.0 o superior
  • Integración PHP+MySQL realizada correctamente.

Y la estructura de archivos es como se muestra a continuación:

rest_api
|-- dao.php
|-- index.php
|-- model.php
|-- service.php


Como primer paso, crear una base de datos y crear la tabla test con la estructura que se muestra a continuación.


Crear el archivo model.php, en donde se especificará una "clase entidad" que corresponderá con la tabla test de nuestra base de datos [Lo sé, no es java =(]. El hecho de no utilizar el concepto de encapsulación es debido a la versión de PHP que se utilizó para este post (4.0).
<?php

class Test {

    var $id;
    var $nombres;
    var $apellidos;

}
También tendremos una clase PHP exclusivamente para el acceso a base de datos. En el archivo dao.php, se implementarán todas las operaciones sobre base de datos.
<?php

class DAO {

    var $server;
    var $db;
    var $dblog;
    var $dbpass;

    function DAO() {
        $this->server = 'localhost';
        $this->db = 'test';
        $this->dblog = 'root';
        $this->dbpass = '123456';
    }

    function connect() {
        $this->cnn = new MySQL(array('host' => $this->server, 'user' => $this->dblog, 'password' => $this->dbpass, 'database' => $this->db));
    }

    function insert($test) {
        $this->connect();
        $sql = "INSERT INTO test(nombres, apellidos) VALUES ('" . $test->nombres . "','" . $test->apellidos . "'" . ")";
        $rs = $this->cnn->query($sql);
    }

    function findAll() {
        $this->connect();
        $sql = "SELECT * FROM test";
        $result = $this->cnn->query($sql);
        $rs = $result->fetchall(MYSQL_ASSOC);
        return $rs;
    }

    function find($id) {
        $this->connect();
        $sql = "SELECT * FROM test WHERE id=" . $id;
        $result = $this->cnn->query($sql);
        $rs = $result->fetchRow(MYSQL_ASSOC);
        return $rs;
    }

    function update($test) {
        $this->connect();
        $sql = "UPDATE test SET nombres ='" . $test->nombres . "', apellidos='" . $test->apellidos . "' WHERE id='" . $test->id . "'";
        $this->cnn->query($sql);
    }

    function delete($id) {
        $this->connect();
        $sql = "DELETE FROM test WHERE id='" . $id . "'";
        $this->cnn->query($sql);
    }

}
Las funciones de la clase DAO serán invocadas a través de una capa de servicio, la cual se encargará de procesar las peticiones y determinar qué tipo de servicio REST es el consultado. El archivo tendrá el nombre service.php.
<?php

require_once("dao.php");
require_once("model.php");

$method = $_SERVER['REQUEST_METHOD'];

$dao = new DAO();
$json_service = new Services_JSON();

//call DAO based on HTTP method
switch ($method) {
    case 'GET':
        $result = $dao->findAll();
        $json_response = $json_service->encode($result);
        echo $json_response;
        break;
    case 'POST':
        $test = new Test();
        $test->nombres = $_POST['nombres'];
        $test->apellidos = $_POST['apellidos'];
        $dao->insert($test);
        break;
    case 'PUT':
        parse_str(file_get_contents("php://input"), $post_vars);
        $test = new Test();
        $test->id = $post_vars['id'];
        $test->nombres = $post_vars['nombres'];
        $test->apellidos = $post_vars['apellidos'];
        $dao->update($test);
        break;
    case 'DELETE':
        parse_str(file_get_contents("php://input"), $post_vars);
        $id = $post_vars['id'];
        $dao->delete($id);
        break;
}
Finalmente, escribimos la parte de código correspondiente al lado del cliente en el archivo index.php. A través de una URL, accedemos a los servicios publicados y utilizamos sus funcionalidades dependiendo de la manera en cómo los invoquemos. (GET/POST/PUT/DELETE).
<?php

$url = "http://localhost/res_api/service.php";

if (!function_exists('http_build_query')) {

    function http_build_query($data, $prefix = null, $sep = '', $key = '') {
        $ret = array();
        foreach ((array) $data as $k => $v) {
            $k = urlencode($k);
            if (is_int($k) && $prefix != null) {
                $k = $prefix . $k;
            }

            if (!empty($key)) {
                $k = $key . "[" . $k . "]";
            }

            if (is_array($v) || is_object($v)) {
                array_push($ret, http_build_query($v, "", $sep, $k));
            } else {
                array_push($ret, $k . "=" . urlencode($v));
            }
        }

        if (empty($sep)) {
            $sep = ini_get("arg_separator.output");
        }

        return implode($sep, $ret);
    }

}

/* GET Client */
$client = curl_init($url);
curl_setopt($client, CURLOPT_RETURNTRANSFER, 1);
$curl_response = curl_exec($client);
echo $curl_response;

/* POST Client */
$curl_post_data = array(
    'nombres' => 'Roger Manuel',
    'apellidos' => 'Rodriguez Alfaro'
);
$client = curl_init($url);
curl_setopt($client, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($client, CURLOPT_POST, 1);
curl_setopt($client, CURLOPT_POSTFIELDS, $curl_post_data);
$curl_response = curl_exec($client);
echo $curl_response;

/* PUT Client */
$client = curl_init($url);
curl_setopt($client, CURLOPT_RETURNTRANSFER, true);
curl_setopt($client, CURLOPT_CUSTOMREQUEST, "PUT");
$curl_put_data = array(
    'id' => '14',
    'nombres' => 'Enma',
    'apellidos' => 'Cruz Ticle'
);
curl_setopt($client, CURLOPT_POSTFIELDS, http_build_query($curl_put_data));
$curl_response = curl_exec($client);
if ($response === false) {
    $info = curl_getinfo($client);
    curl_close($client);
    die('error occured during curl exec. Additioanl info: ' . var_export($info));
}
curl_close($client);
echo $curl_response;


/* DELETE Client */
$client = curl_init($url);
curl_setopt($client, CURLOPT_RETURNTRANSFER, true);
curl_setopt($client, CURLOPT_CUSTOMREQUEST, "DELETE");
$curl_post_data = array(
    'id' => '15'
);
curl_setopt($client, CURLOPT_POSTFIELDS, http_build_query($curl_post_data));
$curl_response = curl_exec($client);
curl_close($client);
echo $curl_response;
Evidentemente, estaría restando la parte de la vista, pero eso ya será tema de otro post. Recuerden que es mi primer post de PHP, por lo que les pido comprensión si algunos conceptos o separaciones de capas no las hago correctamente, la idea es aportar no? :D

martes, 27 de septiembre de 2016

Columnas virtuales con MySQL

Las columnas virtuales son una de las características más resaltantes de MySQL 5.7. Estas columnas tienen la característica de ser definidas en la tabla como el resultado de funciones de otras columnas, por ejemplo:
create database demo;
use demo;
create table tabla(campo1 int, campo2 int, campo3 int generated always as (campo1*campo2) virtual);
insert into tabla values (10, 20, default);
select * from tabla;
+--------+--------+--------+
| campo1 | campo2 | campo3 |
+--------+--------+--------+
|     10 |     20 |    200 |
+--------+--------+--------+

La tabla creada con estas sentencias SQL tendrá dos columnas que almacenarán números enteros y una tercera columna que será el resultado de la multiplicación de las dos columnas anteriores, tal como se muestra en la siguiente figura:


Dichas columnas virtuales son cálculadas sobre la marcha, cada vez que los campos son recuperados. Una de las limitantes es que no podemos utilizar columnas virtuales definidas después de la actual, esto debido a que las columnas virtuales se calculan en el orden en que se definen.

Existe otro tipo de columnas generadas, llamadas columnas almancenadas, pero éstas serán revisadas en otro artículo.

lunes, 19 de septiembre de 2016

Secure Admin must be enabled to access the DAS remotely

En este corto artículo les presento la solución al error:

Secure Admin must be enabled to access the DAS remotely

Que ocurre cuando queremos acceder remotamente a un servidor glassfish recientemente instalado. La solución es tan sencilla como ejecutar el siguiente comando:

asadmin --host [host] --port [port] enable-secure-admin


Recuerden reiniciar el servicio para que los cambios tomen efecto. Saludos!

martes, 13 de septiembre de 2016

How to Install and Configure MySQL Cluster on Windows

MySQL Clúster es una versión de alta disponibilidad, alta redundancia de MySQL adaptada para el entorno de computación distribuida. Usa el motor de almacenamiento NDB Cluster para permitir la ejecución de varios servidores MySQL en un clúster. Este motor de almacenamiento está disponible en las distribuciones binarias de MySQL 5.0, Los sistemas en los que está disponible son Linux, Solaris y Windows. En este artículo vamos a revisar la manera cómo configurarlo.

La infraestructura que utilizaremos será la siguiente:


Los requerimientos son los siguientes:


Las recomendaciones son estas:

  • Desactivar firewall
  • Desactivar antivirus
  • Desinstalar y limpiar versiones anteriores de MySQL

Una vez descargada la versión, descomprimir el contenido en una carpeta en el disco C, a la cual llamaremos mysqlc.

En cada uno de los tres nodos de la figura mostrada anteriormente, crear la estructura de carpetas y su contenido a partir del comando:
C:\>mkdir my_cluster my_cluster\ndb_data my_cluster\mysqld_data my_cluster\conf
C:\>mkdir my_cluster\mysqld_data\mysql my_cluster\mysqld_data\ndbinfo
C:\>copy c:\mysqlc\data\mysql my_cluster\mysqld_data\mysql 
C:\>copy c:\mysqlc\data\ndbinfo my_cluster\mysqld_data\ndbinfo

Nodo Administrador
Crear el archivo config.ini en la carpeta c:\my_cluster\conf con el siguiente contenido:
[ndb_mgmd]
HostName=192.168.1.100
DataDir=c:\my_cluster\ndb_data
Nodeid=1

[Ndbd default]
NoOfReplicas=2

[Ndbd]
HostName=192.168.1.101
Nodeid=3

[Ndbd]
HostName=192.168.1.102
Nodeid=4
[Mysqld]
[Mysqld]
Iniciar el nodo administrador ejecutando el siguiente comando y dejando la consola abierta.
C:\my_cluster>/mysqlc/bin/ndb_mgmd -f conf/config.ini --initial --configdir=c:\my_cluster\conf\
En otra consola de comandos del nodo administrador, ejecutar el programa para verificar el estado de nuestro cluster.
C:\>C:\mysqlc\bin\ndb_mgm -e show
El resultado deberá ser similar al mostrado a continuación:


Nodos de Datos
En cada uno de los nodos de datos, crear el archivo my.cnf en la carpeta c:\my_cluster\conf. El puerto debe ser diferente para cada nodo de dato y de preferencia mayor a 5000.
[mysqld]
ndbcluster
datadir=c:\my_cluster\mysqld_data
port=15002
ndb-connectstring = 192.168.1.100

[mysql_cluster]
ndb-connectstring = 192.168.1.100
Ahora nos conectamos con el nodo administrador ejecutando el siguiente comando en cada uno de los dos nodos de datos
C:\>c:\mysqlc\bin\ndbd -c 192.168.1.100:1186
Sin embargo, al ejecutar en el nodo administrador el siguiente comando.
C:\>C:\mysqlc\bin\ndb_mgm -e show
Podemos notar que, a pesar que la conexión al nodo administrador ha sido hecha, los nodos SQL aún no están habilitados (not connected, accepting connect from any host).


Para levantar las API's, ejecutar en cada nodo de datos el siguiente comando en una consola nueva:
C:\my_cluster>c:\mysqlc\bin\mysqld --defaults-file=conf/my.cnf --console
Al volver a ejecutar el comando de visualización de conexiones en el nodo administrador, la salida será la siguiente:


Ahora sólo resta conectarnos al motor de base de datos de cada nodo utilizando una nueva consola para ejecutar el siguiente comando, teniendo en cuenta el puerto establecido en el archivo my.cnf. En este caso, mostraremos la conexión en el nodo que utiliza el puerto 15002.
c:\my_cluster>c:\mysqlc\bin\mysql -P15002 -u root
Al levantar los dos motores, cualquier sentencia SQL ejecuta en uno de ellos deberá verse automáticamente reflejada en la otra instancia. La siguiente imagen ilustra esto:


Inicialmente, en la consola azul habían las mismas base de datos que en la consola blanca, sin embargo al crear la base de datos clusterdb en esta última, la instancia azul reflejó automáticamente los cambios.

Para finalizar, les dejo los comandos para finalizar el servicio de manera segura:
#Ejecutar por cada nodo de datos, teniendo en cuenta el puerto
C:\my_cluster>c:\mysqlc\bin\mysqladmin -u root -h 127.0.0.1 -P15000 shutdown
#Nodo administrador
C:\my_cluster>c:\mysqlc\bin\ndb_mgm -e shutdown
#Ver puertos
netstat -oan