Páginas

viernes, 18 de enero de 2013

Instalación y pruebas de Solr 4.0.0 en Tomcat

Introducción

Solr es un motor de búsqueda de código abierto basado en la biblioteca Java del proyecto Lucene. Ejecuta sobre un contenedor de servlets, como por ejemplo Apache Tomcat.

La finalidad de Solr es tener un motor de búsqueda interno en nuestro servidor.

Descarga Apache Solr

http://www.eu.apache.org/dist/lucene/solr/4.0.0/apache-solr-4.0.0.tgz

Instalación de Solr 4.0.0 en Apache Tomcat

Para instalar Solr 4.0.0 en Apache Tomcat los pasos a seguir son los siguientes:

1) Descomprimimos el fichero apache-solr-4.0.0.tgz en alguna ubicación de nuestro disco duro, por ejemplo en /home/xxxxxxx/temp/apache-solr-4.0.0
2) Recuperamos el war del directorio dist/apache-solr-4.0.0.war
3) Renombramos el fichero apache-solr-4.0.0.war a solr.war
4) Movemos el fichero solr.war a la carpeta webapps de nuestro tomcat
5) Solr necesita un directorio de trabajo que podemos colocar en cualquier ubicación, en este ejemplo supondremos /home/xxxxxxx/solr
6) Volcamos en la carpeta anterior el contenido de /home/xxxxxxx/temp/apache-solr-4.0.0/example/solr.
7) Tras este volcado, en nuestra carpeta /home/xxxxxxx/solr tendremos:

drwxr-xr-x 2 xxxxxxx xxxxxxx 4096 sep 22 14:36 bin
drwxr-xr-x 4 xxxxxxx xxxxxxx 4096 ene 18 12:34 collection1
-rw-r--r-- 1 xxxxxxx xxxxxxx 2473 sep 22 14:36 README.txt
-rw-r--r-- 1 xxxxxxx xxxxxxx 2203 sep 22 14:36 solr.xml
-rw-r--r-- 1 xxxxxxx xxxxxxx  501 sep 22 14:36 zoo.cfg


8) Ahora arrancamos el Tomcat, con lo que se despliega automáticamente el fichero solr.war que habíamos ubicado en la carpeta webapps del Tomcat en el paso 4). Se generará la carpeta webapps/solr.
9) Ahora paramos el Tomcat.
10) Editamos el fichero webapps/solr/WEBINF/web.xml, sustituyendo:

  <!-- People who want to hardcode their "Solr Home" directly into the
       WAR File can set the JNDI property here...
   -->
  <!--
    <env-entry>
       <env-entry-name>solr/home</env-entry-name>
       <env-entry-value>/put/your/solr/home/here</env-entry-value>
       <env-entry-type>java.lang.String</env-entry-type>
    </env-entry>
   -->

por:

  <!-- People who want to hardcode their "Solr Home" directly into the
       WAR File can set the JNDI property here...
   -->

    <env-entry>
       <env-entry-name>solr/home</env-entry-name>
       <env-entry-value>/home/
xxxxxxx/solr</env-entry-value>
       <env-entry-type>java.lang.String</env-entry-type>
    </env-entry>


11) Arrancamos Tomcat y ya podríamos acceder a la página de inicio del portal, http://localhost:8080/solr/admin

Utilización de Solr

Una vez instalado Solr, podemos subir y buscar documentos fácilmente desde Java. Necesitaremos las librerías:

  <dependencies>
      <dependency>
          <groupId>org.apache.solr</groupId>
          <artifactId>solr-common</artifactId>
          <version>1.3.0</version>
      </dependency>
      <dependency>
          <groupId>org.apache.solr</groupId>
          <artifactId>solr-core</artifactId>
          <version>1.4.0</version>
      </dependency>
      <dependency>
          <groupId>org.slf4j</groupId>
          <artifactId>slf4j-log4j12</artifactId>
          <version>1.7.2</version>
      </dependency>
  </dependencies>


El código de la clase ejemplo sería:

import java.io.IOException;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.impl.CommonsHttpSolrServer;
import org.apache.solr.client.solrj.impl.XMLResponseParser;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.common.SolrDocumentList;
import org.apache.solr.common.SolrInputDocument;

public class AppTest {

    /**
     * URL del servidor Solr
     */
    private static final String URL_SOLR = "http://localhost:8080/solr";

    public static void main (String args[]) throws SolrServerException, IOException {
   
        //Conectamos con el servidor Solr
        final CommonsHttpSolrServer serverSolr = new CommonsHttpSolrServer(URL_SOLR);
        serverSolr.setParser(new XMLResponseParser());
       
        //Creamos un par de documentos
        creaDocQuijote(serverSolr);
        creaDocBuscon(serverSolr);
       
        //Buscamos documentos por nombre
        buscaDocNombre(serverSolr, "hidalgo");
       
        //Buscamos documentos por contenido
        buscaDocContenido(serverSolr, "Mancha");
       
        //Borramos todo el contenido del servidor (por aquello de dejarlo limpio y poder repetir las inserciones)
        serverSolr.deleteByQuery("*:*");
    }
   
    private static void creaDocQuijote(final CommonsHttpSolrServer serverSolr) throws SolrServerException, IOException {
       
        final SolrInputDocument doc = new SolrInputDocument();
        doc.addField("id", "000001");
        doc.addField("name", "El ingenioso hidalgo don Quijote.txt");
        doc.addField("text", "En un lugar de la Mancha de cuyo nombre");
        serverSolr.add(doc);
        serverSolr.commit();
    }
   
    private static void creaDocBuscon(final CommonsHttpSolrServer serverSolr) throws SolrServerException, IOException {
       
        final SolrInputDocument doc = new SolrInputDocument();
        doc.addField("id", "000002");
        doc.addField("name", "Buscón.txt");
        doc.addField("text", "Yo, señora, soy de Segovia. Mi padre se llamó Clemente Pablo");
        serverSolr.add(doc);
        serverSolr.commit();
    }
   
    private static void buscaDocNombre(final CommonsHttpSolrServer serverSolr, final String nombre) throws SolrServerException{
       
        final SolrQuery query = new SolrQuery();
        query.setQuery("name:quijote.txt");
        final QueryResponse rsp = serverSolr.query(query);
        final SolrDocumentList docList = rsp.getResults();
        System.out.println("Encontrados "+docList.size()+" documentos con el nombre '"+nombre+"'");
    }
   
    private static void buscaDocContenido(final CommonsHttpSolrServer serverSolr, final String contenido) throws SolrServerException {
       
        final SolrQuery query = new SolrQuery();
        query.setQuery("text:Mancha");
        final QueryResponse rsp = serverSolr.query(query);
        final SolrDocumentList docList = rsp.getResults();
        System.out.println("Encontrados "+docList.size()+" documentos con el contenido '"+contenido+"'");
    }
}


Enlaces de interés

http://stackoverflow.com/questions/10026014/apache-solr-configuration-with-tomcat-6-0
http://www.solrtutorial.com/

sábado, 12 de enero de 2013

Primeros pasos con Apache Velocity

En esta entrada vamos a detallar un ejemplo de uso de Apache Velocity. Esta herramienta nos permite generar fácilmente documentos xml, html, sql, csv, txt, etc.

Apache Velocity renderiza una plantilla con ciertos datos. Las plantillas deberán ser generadas previamente en base al lenguaje VTL (Velocity Template Language).

VTL nos permite generar informes muy grandes con poco esfuerzo.

Supongamos que nos interesa enviar el informe siguiente por correo electrónico:

+------+------------+--------+
| Año  | Trimestre  | Ventas |
+------+------------+--------+
| 2005       T1         1000 |
| 2005       T2          200 |
| 2005       T3           50 |
| 2005       T4            5 |
+------+------------+--------+


Como primera aproximación montaríamos una plantilla simple, donde las líneas de contenido fijo aparecen tal cual, mientras que en aquellas líneas donde hay conenido variable (año, trimestre y unidades) hemos insertado unas variables  respetando los espacios en blanco que mantienen el encolumnado.

Las diferentes líneas del informe se generan recorriendo los elementos de la lista $listaVentas, cada elemento de la lista se carga en la variable $venta, y cada venta tiene dos atributos: $venta.trim y $venta.unidaddes.

#***************
Plantilla simple
***************#
+------+------------+--------+
| Año  | Trimestre  | Ventas |
+------+------------+--------+
#foreach($venta in $listaVentas)
| $anno       $venta.trim         $venta.unidades |
#end
+------+------------+--------+


Seguidamente montaríamos el código java, en donde cabe destacar que iniciamos el motor velocity fijando el encoding tanto de la plantilla de entrada como del documento renderizado a generar:

package org.edu.velocity.app;

import java.io.StringWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;

public class Test {

    public static void main(String[] args) {

        //Instanciamos el motor velocity
        final VelocityEngine ve = new VelocityEngine();
       
        //Definimos el encoding de entrada y salida
        final Properties props = new Properties();
        props.put("output.encoding","UTF-8");
        props.put("input.encoding","UTF-8");
       
        //Inicializamos el motor velocity con el encoding deseado
        ve.init(props);
       
        //Cargamos la plantilla
        final Template t = ve.getTemplate("informe-simple.vm");
        //final Template t = ve.getTemplate("informe-alineado.vm");
       
        //Preparamos los datos
        final List<Object> listaVentas = new ArrayList<Object>();
        Map<String, String> map = new HashMap<String, String>();
        map.put("trim", "T1");
        map.put("unidades", "1000");
        listaVentas.add(map);
        map = new HashMap<String, String>();
        map.put("trim", "T2");
        map.put("unidades", "200");
        listaVentas.add(map);
        map = new HashMap<String, String>();
        map.put("trim", "T3");
        map.put("unidades", "50");
        listaVentas.add(map);
        map = new HashMap<String, String>();
        map.put("trim", "T4");
        map.put("unidades", "5");
        listaVentas.add(map);
       
        //Creamos el contexto y le metemos los datos
        final VelocityContext context = new VelocityContext();
        context.put("listaVentas", listaVentas);
        context.put("anno", "2005");
       
        //Renderizamos la plantilla
        final StringWriter writer = new StringWriter();
        t.merge( context, writer );
       
        //Visualizamos la plantilla
        System.out.println(writer.toString());

}


Al ejecutar este código tendríamos la siguiente salida:

+------+------------+--------+
| Año  | Trimestre  | Ventas |
+------+------------+--------+
| 2005       T1         1000 |
| 2005       T2         200 |
| 2005       T3         50 |
| 2005       T4         5 |
+------+------------+--------+


La salida es casi lo que queríamos, pero al variar la longitud de los números de la última columna, tiene un aspecto incorrecto. Vamos a reajustar la plantilla con un control del tamaño de la variable $venta.unidades. Con lo que la plantilla alineada quedaría:

#*****************
Plantilla alineada
*****************#
+------+------------+--------+
| Año  | Trimestre  | Ventas |
+------+------------+--------+
#foreach($venta in $listaVentas)
#if($venta.unidades.length()==4)
| $anno       $venta.trim         $venta.unidades |
#{elseif}($venta.unidades.length()==3)
| $anno       $venta.trim          $venta.unidades |
#{elseif}($venta.unidades.length()==2)
| $anno       $venta.trim           $venta.unidades |
#{else}
| $anno       $venta.trim            $venta.unidades |
#end
#end
+------+------------+--------+


Si ejecutamos de nuevo el informe tendremos la salida corregida:

+------+------------+--------+
| Año  | Trimestre  | Ventas |
+------+------------+--------+
| 2005       T1         1000 |
| 2005       T2          200 |
| 2005       T3           50 |
| 2005       T4            5 |
+------+------------+--------+


Librerías.

Para lanzar este ejemplo hemos necesitado las librerías:

velocity-1.7.jar
commons-lang-2.4.jar
commons-collections-3.2.1.jar

Enlaces.

Más información sobre Apache Velocity y VTL en:

http://velocity.apache.org/
http://velocity.apache.org/engine/devel/vtl-reference-guide.html

Conclusiones.

Para generar documentos xml, html, sql, csv y txt, es una herramienta muy potente. La única limitación que le encuentro (sin haberle dedicado mas de una hora al tema), es que si pretendemos generar txt habrá que limitarse a cosas muy simples, dado que en cuanto el documento objetivo es un poco mas complejo la cosa se complica.

No quiero ni pensar qué habría que meter en VTL para generar un informe con varias columnas numéricas que tuviesen que salir alineadas como en el siguiente ejemplo:

+------+-----------+----------+---------+-------+
| Año  | Trimestre | Entradas | Salidas | Stock |
+------+-----------+----------+---------+-------+

| 2005 |     T1    |     1000 |     100 |   900 |
| 2005
|     T2    |      200 |      50 |   150 |
| 2005 |     T3    |       50 |      20 |    30 |
| 2005 |     T4    |        5 |       3 |     2 |
+------+-----------+----------+---------+-------+


La cosa también sería muy compleja si alguien quiere que se generen documentos en txt con justificación completa (es un ejemplo muy rebuscado, me doy cuenta), similares al siguiente ejemplo:

Estimado Juan Español Español.

Nos dirigimos a  vd.  para comunicarle que el
día  17 de  Abril de 2013 a  transferiremos a
la cuenta 0182-0098-XX-XXXXXXXXXX la cantidad
de  1.355,22€ liquidando de ese modo la deuda
pendiente.

En Madrid a 01 de Enero de 2013.

Supongo que las librerías velocity-tools y .