Ejemplo de CountDownLatch

Hola Amigos, esta semana tengo un simple ejemplo de uso de la clase CountDownLatch.

Esta clase simplemente nos ayuda a sincronizar operaciones dentro de varios threads que necesiten ejecutarse en un momento dado.

El siguiente ejemplo muestra la creación de 50 threads. Como verán, dentro del método run() hay una porción de código que llama a la funcion await(). Esto ayuda a que cada thread no ejecute el siguiente segmento de código hasta que la señal del CountDownLatch sea lanzado.

El código fuente lo pueden encontrar en acá.
Mas información la pueden encontrar en:

import java.util.concurrent.CountDownLatch;
 
public class CountDownLatchExample {
 
	public static void main(String... args) {
		// Instantiating countdownlatch class.
		CountDownLatch countDown = new CountDownLatch(1);
		System.out.println("Creating threads...");
 
		// going to create 50 threads.
		for(int i=0;i<50;i++) {
			Worker worker = new Worker(countDown, "Worker #"+i);
			new Thread(worker).start();
		}
 
		// Now start all threads at the same time.
		countDown.countDown();
 
	}
 
	/**
	 * This class is just a simple dummy thread.
	 */
	private static class  Worker implements Runnable {
		CountDownLatch startLatch;
		String name;
 
		public Worker(CountDownLatch startLatch, String name) {
			super();
			this.startLatch = startLatch;
			this.name = name;
		}
 
		public void run() {
			try {
				// Going to wait until the signal is being fired.
				this.startLatch.await();
			} catch (InterruptedException e) {
				throw new RuntimeException(e);
			}
			System.out.println("Running " + name+".");
		}
	}
}

Custom (Personalizado) XML DataSource con JasperReports

Hola amigos,

Aca un ejemplo muy sencillo de como implementar las interfaces JRDataSource y JRRewindableDataSource de JasperReports para iterar un documento xml.

Clase CustomXMLDataSource

Como verán el constructor de la clase recibe, una instancia de la interface (XML) Document, luego extrae el nodo raiz y después obtiene la lista de los nodos hijos.

Para navegar por la lista de nodos se usa una variable llamada index, cuyo valor inicial es -1 para que la primera vez que se llame al método next(), este apunte al primer elemento de la lista.

El método getFieldValue usa los atributos del nodo actual para ser retornado hacia afuera. Para esto se usa el description del JRField. Lo que me gusta de este metodo es que uno acá puede implementar cualquier lógica que uno desee para obtener información de cualquier tipo de fuente de datos. Acá se podría aplicar cosas mas complejas como Xpath, o talvez hacer algo simple como buscar un nodo basado en el JRField. Alguien me comento que usar XPath puede ser un poco mas lento, es por eso que esta interface es muy importante para nosotros.

El método moveFirst es el que se encarga hacer que data source vuelva al primer nodo. Esta parte me gusto mucho, por que vi que por defecto el JRDataSource de JasperReport no implementa este metodo, seguro para hacerlo mas genérico al momento de trabajar con fuentes de datos que no sean tan fácil de manejar como una lista:).

Pruebas de unidad y código fuente

En las pruebas de unidad podrán apreciar varios casos de uso de la clase.Como ser: utilizando un xml que tiene un solo nodo, cuando son varios, etc.

El código fuente esta disponible en: https://github.com/rhuanca/examples/tree/master/jasper-reports

Saludos,
Renan

package renidev.examples.jaspereports;
 
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
 
import net.sf.jasperreports.engine.JRDataSource;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JRField;
import net.sf.jasperreports.engine.JRRewindableDataSource;
 
/**
 * This is a sample custom data source for xml.
 * @author Renan Huanca
 *
 */
public class CustomXMLDataSource implements JRDataSource, JRRewindableDataSource {
 
	private NodeList childNodes;
	private int index;
 
	public CustomXMLDataSource(Document document) {
		// Note: document.getFirstChild() will return the root node.
		this.childNodes = document.getFirstChild().getChildNodes();
		this.index = -1;
	}
 
	/**
	 * Implementation of the getFieldValue method.
	 * In this case this method is extracting the value 
	 * from the note's attributes.
	 */
	public Object getFieldValue(JRField jrField) throws JRException {
		String nodeName = jrField.getDescription();
		return childNodes.item(index).getAttributes().getNamedItem(nodeName).getTextContent();
	}
 
	public boolean next() throws JRException {
		++this.index;
		return this.index < childNodes.getLength();
	}
 
	public void moveFirst() throws JRException {
		this.index = -1;
	}
}
package renidev.examples.jasperreports;
 
import static org.junit.Assert.*;
import org.junit.Test;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JRField;
import net.sf.jasperreports.engine.design.JRDesignField;
import renidev.examples.jaspereports.CustomXMLDataSource;
 
 
public class CustomXMLDataSourceTest {
 
	@Test
	public void single_case() throws JRException{
		String xml ="<root></root>";
		CustomXMLDataSource dataSource = new CustomXMLDataSource(SimpleDocumentBuilder.buildDocument(xml));
		assertFalse(dataSource.next());
	}
 
	@Test
	public void one_child_node() throws JRException{
		String xml ="<friends><friend name='carlos'/></friends>";
		CustomXMLDataSource dataSource = new CustomXMLDataSource(SimpleDocumentBuilder.buildDocument(xml));
		assertTrue(dataSource.next());
		assertEquals("carlos", dataSource.getFieldValue(createField("name")));
		assertFalse(dataSource.next());
	}
 
	@Test
	public void two_child_nodes() throws JRException{
		String xml =
				"<friends>" +
					"<friend name='carlos'/>" +
					"<friend name='gonzalo'/>" +
				"</friends>";
		CustomXMLDataSource dataSource = new CustomXMLDataSource(SimpleDocumentBuilder.buildDocument(xml));
		JRField nameField = createField("name");
 
		// reading data data
		assertTrue(dataSource.next());
		assertEquals("carlos", dataSource.getFieldValue(nameField));
		assertTrue(dataSource.next());
		assertEquals("gonzalo", dataSource.getFieldValue(nameField));
 
		// checking there is no more data 
		assertFalse(dataSource.next());
	}
 
	@Test
	public void two_child_nodes_and_two_attributes() throws JRException{
		String xml =
				"<friends>" +
					"<friend name='carlos' from='work'/>" +
					"<friend name='gonzalo' from='raquet'/>" +
				"</friends>";
		CustomXMLDataSource dataSource = new CustomXMLDataSource(SimpleDocumentBuilder.buildDocument(xml));
		JRField nameField = createField("name");
		JRField fromField = createField("from");
 
		// reading data data
		assertTrue(dataSource.next());
		assertEquals("carlos", dataSource.getFieldValue(nameField));
		assertEquals("work", dataSource.getFieldValue(fromField));
		assertTrue(dataSource.next());
		assertEquals("gonzalo", dataSource.getFieldValue(nameField));
		assertEquals("raquet", dataSource.getFieldValue(fromField));
 
		// checking there is no more data 
		assertFalse(dataSource.next());
	}
 
 
	@Test
	public void two_child_nodes_and_rewind() throws JRException{
		String xml =
				"<friends>" +
					"<friend name='carlos'/>" +
					"<friend name='gonzalo'/>" +
				"</friends>";
 
		CustomXMLDataSource dataSource = new CustomXMLDataSource(SimpleDocumentBuilder.buildDocument(xml));
		JRField nameField = createField("name");
 
		// bypassing two nodes
		assertTrue(dataSource.next());
		assertTrue(dataSource.next());
 
		// checking there is no more data 
		assertFalse(dataSource.next());
 
		// rewind the data source
		dataSource.moveFirst();
 
		// reading data data
		assertTrue(dataSource.next());
		assertEquals("carlos", dataSource.getFieldValue(nameField));
		assertTrue(dataSource.next());
		assertEquals("gonzalo", dataSource.getFieldValue(nameField));
	}
 
	public JRField createField(String description){
		JRDesignField field = new JRDesignField();
		field.setDescription(description);
		return field;
	}
}

Simple (DOM) DocumentBuilder para Java

Hola Amigos,

Acá comparto una clase que ayuda a crear una instancia de un java DOM Document.

Estas son las características de la clase:

  1. Te quita todo el tedioso trabajo de hacer explícitamente el catch de las excepciones, haciendo que tu código sea mas legible y entendible.
  2. Las excepciones son manejadas con un solo handler, haciendo que puedas mejorar la clase aumentar varios builders y manejar las excepciones en un solo método.
  3. También pueden apreciar que la instancia del DocumentBuilderFactory es inicializado estáticamente.

Luego mas abajo podrán ver las pruebas de unidad con solo casos mas simple y el manejo de una excepción.

Si desean bajar el código, este esta disponible en: https://github.com/rhuanca/XMLUtilities

package renidev.utils.xml;
 
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
 
/**
 * This class helps to create a instance of a DOM document instance
 * simplifying the way to create it.
 * @author Renan Huanca
 */
public class SimpleDocumentBuilder {
    private static DocumentBuilder documentBuilder;
 
    static {
        try {
            documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
        } catch (ParserConfigurationException e) {
            throw new RuntimeException("Unable to get document builder. - "+ e.getMessage(), e);
        }
    }
 
    /**
     * Allow to create a DOM Document instance with the given parameter.
     * 
     * @param xml
     * @return
     */
    public static Document buildDocument(String xml) {
        Document doc = null;
        try {
            doc = documentBuilder.parse(new ByteArrayInputStream(xml.getBytes()));
        } catch (Exception e) {
            handleException(e);
        } 
        return doc;
    }
 
    /**
     * Allow to create a DOM Document instance with the given parameter.
     * @param stream
     * @return
     */
    public static Document buildDocument(InputStream stream) {
        Document doc = null;
        try {
            doc = documentBuilder.parse(stream);
        } catch (Exception e) {
            handleException(e);
        } 
        return doc;
    }
 
    public static void handleException(Exception e){
        if(e instanceof IOException) {
            throw new RuntimeException("Unable to read xml - " + e.getMessage(), e);
        } else if (e instanceof SAXParseException) {
            SAXParseException exception = (SAXParseException) e;
            throw new RuntimeException("Unable to parse xml - Line: " + 
                    exception.getLineNumber() + " - " + e.getMessage(), e);
        } else if ( e instanceof SAXException) {
            throw new RuntimeException("Unable to parse xml - " + e.getMessage(), e);
        } else {
            throw new RuntimeException(e);
        }
    }
}
package renidev.utils.xml;
 
import org.junit.Test;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import static org.junit.Assert.*;
 
public class SimpleDocumentBuilderTest {
 
    @Test
    public void one_node(){
        String xml = "<data>hello world</data>";
        Document doc = SimpleDocumentBuilder.buildDocument(xml);
        Node dataNode = doc.getFirstChild();
        assertEquals(1, dataNode.getChildNodes().getLength());
        assertEquals("hello world", dataNode.getTextContent());
    }
 
    @Test
    public void one_level(){
        String xml = "<data><child>hello world</child></data>";
        Document doc = SimpleDocumentBuilder.buildDocument(xml);
        Node dataNode = doc.getFirstChild();
        assertEquals(1, dataNode.getChildNodes().getLength());
        assertEquals("hello world", dataNode.getFirstChild().getTextContent());
    }
 
    @Test 
    public void test_exception(){
        String xml = "<data>hello world<data>";
        try {
            @SuppressWarnings("unused")
            Document doc = SimpleDocumentBuilder.buildDocument(xml);
        } catch (RuntimeException e) {
            assertEquals("Unable to parse xml - Line: 1 - XML document structures must start and end within the same entity.", e.getMessage());
            return;
        }
        fail("No exception was throw");
    }
}