Loading several Rails applications into a single Tomcat container with Trinidad 0

Posted by david

Sometimes you meet that kind of people that complain about the lack of any feature in an open source project but they don't do anything to solve the problem. Last releases of Trinidad try the solve one of that complaints, so that people have one excuse less to use it :)

The goal of the project was pretty simple, allow to run a Rails application under Apache Tomcat without generating a war file in a more familiar "rails way". But that guy complained because he wanted to run several applications into the same instance of Tomcat, and actually he was partially right, Tomcat is a powerful application server and it can run several applications at once, thus, why don't allow to Trinidad to run several Rails applications?

After a few changes in its architecture, Trinidad allows to run as many Rails applications as Tomcat can support into the same instance. We just need add the application's context and their system path to the Trinidad's configuration file and run Trinidad with the option to read that file.

For instance, if we want to load three applications we had to add this nodes to the yaml file:


---
  :web_apps:
    :default:                                         # context path is '/'
      :web_app_dir: 'rails_apps/mock'
    :mock1:                                         # context path is '/mock1'
      :web_app_dir: 'rails_apps/mock1'
    :mock2:
      :web_app_dir: 'rails_apps/mock2'
      :context_path: '/mock2'

and run Trinidad with the configuration option:


$ jruby -S trinidad --config

That's it. Hope it will useful. Btw all the configuration options can be found in the Trinidad's wiki.

Improving JRuby 1.9 compatibility 0

Posted by david

For some time I've been contributing with the JRuby team fixing bugs and adding some new features, and most of my work has been focused in Ruby 1.9 compatibility.

Yesterday, Nick Sieger wrote a blog about some things to look at JRuby 1.4, and I'd like to add my "from novice to novice" piece of advice.

Run and fix specs can be a tedious task, you can take a look at the latest patches I sent, so I wrote a little script to help me to run them easily:

It has two usage options, it runs all specs under a directory, or you can specify one or some of them to run. I know it's not perfect but it works for my daily work.

Hope it can help you and I'll see your patches soon.

Using Hudson as Rails CI server 3

Posted by david

For some time, Hudson has become my favorite continuous integration server. It's easy to configure and provides a handful of really interesting plug-ins. The only thing that I missed was the possibility of use Rake as a project build tool and thus I'll be able to take my java, ruby or rails projects into the same CI server.

Well, past weekend I had too much spare time so I decided to develop my first Hudson plugin and this morning I've released the first version of the Hudson Rake plugin.

Once you have installed Hudson you just need to donwload the plug-in and upload it from the Manage Hudson section:

When the plugin is avalable it detects your ruby instances installed from your PATH but it allows you to add other ruby or jruby paths:

Finally you just need to select the Invoke Rake option into the project configuration and select the tasks that you want to Hudson executes:

That's it, you are ready to go with Rake, Hudson and the Continuous Integration Game.

Usa ruby para testear tu código java 2

Posted by david

Aunque navegando por la web se pueden encontrar artículos sobre cómo usar rspec para testear código java y ya existe algún framework de bdd para java, desde hace unas semanas existe una librería que nos da las mejores herramentas de test que ruby posee para aplicarlas con nuestro código java.

JtestR es una librería que integra jruby con rspec, dust, mocha y ActiveSuport, y permite ejecutar nuestros test escritos en ruby con herramentas de automatización como Ant o Maven.

Para ejecutarlos con Ant es tan facil como incluir una nueva tarea en nuestro fichero de configuración:

<target name="test" description="Runs all tests">
  <taskdef name="jtestr" classname="org.jtestr.ant.JtestRAntRunner" classpath="lib/jtestr.jar"/>

  <jtestr tests="test/ruby"/>
</target>

Pero lo que más me gusta de este framework es poder usar rspec para testear mis clases java, un sencillo ejemplo podría ser este:

import java.util.HashMap

describe HashMap, "is empty" do
  before :each do
    @map = mock(HashMap)
  end

  it "should be empty" do
    @map.stubs(:size).returns(0)

    @map.should be_empty
  end
end

Una herramienta a tener en cuenta si, como yo, pasas el día entre código java y código ruby :-).

BDD & RSpec rulez 0

Posted by david

¿Por qué deberías pensar en usar BDD y RSpec?

Un ejemplo vale más que mil palabras

Rails plugin: atom_pub_server 0

Posted by david

Update: he cambiado el repositorio y ahora el plugin se puede instalar desde GitHub:

   $script/plugin install git://github.com/calavera/atompub-server.git

Coincidiendo con el lanzamiento de la api de 11870.com tenía ganas de liberar un plugin para Rails que me ha ayudado a experimentar con AtomPub.

Como no soy muy original con los nombres lo he llamado atom_pub_server y básicamente sirve para empezar a implementar AtomPub dentro de una aplicación desarrollada con Rails. Ya que el punto de entrada del protocolo es su documento de servicio este plugin sirve para generar un documento de servicio a partir de los controllers de una aplicación. A partir de ahí, y gracias al genial soporte de REST por parte de rails y a alguna de las novedades de la versión 2.0 implementar un servidor de Atom Publishing Protocol con una aplicación rails es francamente sencillo.

Pero para ver como funciona el plugin lo mejor es ver un ejemplo. Empezaremos instalándolo en la aplicación desde el repositorio:

    $script/plugin install http://svn.thinkincode.net/rails/plugins/atom_pub_server

Supongamos que nuestra aplicación tiene dos controllers, uno llamado PostsController y otro llamado ServicesController. El primero será en encargado de gestionar una colección de recursos, así que le añadiremos el siguiente método:

    class PostsController < ApplicationController
      acts_as_collection :title => 'posts', :workspace => 'blog',
        :href=> 'http://myblog', :accept => Mime::ATOM_ENTRY
    ...

Al marcar este primer controlador para que actúe como una colección automáticamente se le añade un filtro anterior a los métodos de crear y actualizar un elemento. El método que invoca el filtro se debe llamar filter_content_type y los nombres de los métodos sobre los que se ejecuta se pueden configurar como variables de entorno, siendo por defecto create y update.

El segundo controlador será el encargado de generar el documento de servicio, así que lo marcaremos como tal:

    class ServicesController < ApplicationController
        acts_as_service_document
    ...

Finalmente, para que el controlador genere el documento de servicio solo tendremos que llamar al método "service_document" que le aporta el plugin dentro de otro de los métodos del controlador:

    def index
      render :xml => service_document
    end

y nos generará un xml parecido a este:

  <service xmlns:atom='http://www.w3.org/2005/Atom' xmlns='http://www.w3.org/2007/app'>
    <workspace>
      <atom:title>blog</atom:title>
      <collection href='http://myblog'>
          <atom:title>posts</atom:title>
          <accept>application/atom+xml;type=entry</accept>
     </collection>
    </workspace>
  </service>

Además, este plugin añade varios tipos Mime necesarios para la cumplir especificación de AtomPub:

    Mime::ATOM_ENTRY == 'application/atom+xml;type=entry'
    Mime::ATOM_SVC == 'application/atomsvc+xml'
    Mime::ATOM_CAT == 'application/atomcat+xml'

Para finalizar, he decidido sobreescribir uno de los nuevos helpers de rails, concretamente el que nos ayuda a escribir documentos Atom. El método original no permite añadir nuevos espacios de nombres siendo su llamada tal que así:

    atom_feed do |feed|
    ...

mientras que con este plugin podríamos escribir:

    atom_feed( 'xmlns:app' => 'http://www.w3.org/2007/app') do |feed|
    ...

Cabe decir que el método para sobreescribir 'atom_feed' sólo funciona con la versión actual de Rails (2.0RC) y posteriores. En el fichero README del plugin se puede encontrar una documentación mucho más detallada. Cualquier duda, sugerencia o mejora es bienvenida.

Consumir microformatos con XQuery

Posted by david

XQuery es un lenguaje para hacer consultas bien formadas sobre documentos xml. Combinado con TagSoup, que permite parsear cualquier documento html como si fuera xml, y un procesador de documentos como Nux es muy facil consumir microformatos de cualquier web.

Como supuesto veamos como consumir el hcard de uno de mis sitios favoritos en 11870, el café Costello.

XQuery está basado en XPath pero además nos permite hacer consultas más cercanas al sql. La consulta para extraer del dom de un documento xml un elemento cuyo atributo contenga la palabra vcard podría ser así:

    String query = "declare namespace xhtml=\"http://www.w3.org/1999/xhtml\"; \n" +
        " for $data in //xhtml:* \n"+
        " where contains($data/@class, \"vcard\") \n" +
                " return  { data($data) } ";

Refinandola un poco más podríamos decir que solo queremos mostrar unos cuantos nodos, como los que contengan el nombre, dirección, localidad, provincia y país del sitio:

    String query = "declare namespace xhtml=\"http://www.w3.org/1999/xhtml\"; \n" +
        "for $data in //xhtml:* \n"+
    " where contains($data/@class, \"vcard\") \n"+
    " return <vcard> \n" + 
        " <fn> { data($data//xhtml:*[contains(@class, \"fn\")]) } </fn>" +
        " <street-address> { data($data//xhtml:*[contains(@class, \"street-address\")]) } </street-address>" +
        " <locality> { data($data//xhtml:*[contains(@class, \"locality\")]) } </locality>" +
        " <region> { data($data//xhtml:*[contains(@class, \"region\")]) } </region>" +
        " <country-name> { data($data//xhtml:*[contains(@class, \"country-name\")]) } </country-name>" +
    " </vcard> ";

Una vez tenemos la consulta, podemos obtener el html haciendo una llamada por get a la dirección de nuestra página:

    GetMethod get = new GetMethod("http://11870.com/pro/20770"); 
    get.setFollowRedirects(true); 

    HttpClient httpClient = new HttpClient();
    httpClient.executeMethod(get); 
    InputStream in = get.getResponseBodyAsStream(); 

Y transformar el html en xml para ejecutar nuestra consulta xquery:

    XMLReader parser = new org.ccil.cowan.tagsoup.Parser();
    Document doc = new Builder(parser).build(in);
    Nodes results = XQueryUtil.xquery(doc, query);

La variable results almacenaría el resultado de ejecutar la consulta, donde quedaría un xml parecido a este:

    <vcard>
        <fn>Costello Cafe</fn>
        <street-address>Calle del Caballero de Gracia 10</street-address>
        <locality>Madrid</locality>
        <region>Madrid</region>
        <country-name>España</country-name>
    </vcard>

TagSoup facilita bastante la tediosa tarea de parsear un html ya que transforma hasta los documentos peor formados, y con unos conocimientos de xquery las posiblidades son casi infinitas. Todo el código del ejemplo se puede descargar de aqui.

Validando feeds contra el w3c 1

Posted by david

Ultimamente he pasado demasiado tiempo jugando con los feeds de 11870, pero aunque tengo el proceso de creación bastante trillado y lo he dejado muy DRY nunca está de más validarlos.

El w3c tiene una web que te permite validar un feed alojado en una determinada url o pastearle el código que este genera dentro den un textarea. La principal pega es que si tu url es la de tu ordenador de desarrollo no la acepta y tienes que optar por la segunda opción.

Por suerte se les ocurrió liberar un api para no tener que estar copiando y pegando cada vez que queramos validar un feed que estemos desarrollando. Con esta api me he creado un script que indicándole el host y la ruta del feed, lo descarga y lo intenta validar contra el w3c, no es nada del otro mundo y seguro que es muy mejorable pero a mi me ha sido bastante útil.

Básicamente se trata de recoger el xml que genera el feed con la siguiente instrucción:

@response = Net::HTTP.get_response ARGV[0], ARGV[1], 80

Y enviarlo al validador:

v = W3C::FeedValidator.new
v.validate_data @response.body.to_s 

Una vez hecho esto podremos comprobar si el feed es válido con el método valid? y acceder a los mensajes a través de las variables errors, warnings e informations.

NOTA: El validador del w3c no funciona muy bien y de vez en cuando da algún error al validar, solo hay que insistir un poco hasta que pilla vuestro feed ;)

Java 6 y tu dni electrónico

Posted by david

Ahora que han pasado unos meses desde el lanzamiento de Java 6 y que en casi en cualquier provincia puedes expedir un dni electrónico ya podemos empezar a pensar como hacer uso de ambos.

Dentro de nuestro dnie se encuentran varios certificados digitales para firma electrónica o autenticación, para acceder a ellos necesitamos es un lector de tarjetas que acepte nuestro documento.

Una de las nuevas incorporaciones en java 6, y de las menos publicitadas, es el soporte nativo para acceder al almacen de claves de windows. Hasta ahora teníamos que usar complejas DLLs de windows para acceder a este almacen, pero con esta nueva release es tan fácil como esto:

KeyStore keyStore = KeyStore.getInstance("Windows-MY");
keyStore.load(null, null);

Como no todos en este mundo usamos ese sistema operativo, dentro de nuestro dnie también podremos encontrar los certificados dentro de una librería PKCS#11 llamada opensc-pkcs11.so que podremos usar en cualquier otro sistema. El proceso de acceder a una librería PKCS#11 es un poco más tedioso pero hay muy buenos artículos en la red que lo explican . Una aproximación rápida podría ser que tenemos que añadir esta librería como un proveedor de certificados a nuestro almacen de claves PKCS#11 y luego acceder a este para recuperar los certificados. Para añadir la librería a nuestro almacén necesitaríamos algo como esto:

String pkcs11config = "name = DNIE\nlibrary = opensc-pkcs11.so ";
InputStream confStream = new ByteArrayInputStream(pkcs11config.getBytes());

Class sunPkcs11Class = Class.forName("sun.security.pkcs11.SunPKCS11");
Constructor pkcs11Constr = sunPkcs11Class.getConstructor(InputStream.class);

Provider pkcs11Provider = (Provider) pkcs11Constr.newInstance( confStream );
Security.addProvider(pkcs11Provider);

para acceder a este almacén:

KeyStore keyStore = KeyStore.getInstance("PKCS11");
keyStore.load(null, password);

Una vez cargado nuestro almacén de claves correspondiente podríamos acceder a todos los certificados que contiene a través de sus alias:

Enumeration enumeration = keyStore.aliases();
while (enumeration.hasMoreElements()) {

String alias = enumeration.nextElement().toString();
Certificate[] certs = store.getCertificateChain( alias );

}

Y con esto ya tendríamos acceso a los certificados de nuestro dni electrónico y podríamos ir pensando en como darles uso.

Actualización: Si nuestros usuarios usan Mac OS X y queremos acceder al almacén de claves de este sistema tendríamos que usar el siguiente código:

KeyStore keyStore = KeyStore.getInstance("KeychainStore", "Apple");
keyStore.load(null, null);

Crear fixtures fácilmente 2

Posted by david

Ruby on rails nos proporciona muchas comodidades a la hora de hacer desarrollos orientados a test. Quizá uno de los mayores inconvenientes sea tener que escribir nuestas fixtures, porque al trabajar con modelos algo complicados puede que no sean todo lo válidas que nos gustaría.

Fixturease es una gema que nos permite crear fixtures directamente a partir del modelo de nuestra aplicación.

Su instalación es muy sencilla:

sudo gem install fixturease

Una vez instalada, nos situamos en el directorio raiz de nuesta aplicación y ejecutamos el script fixturease.rb. Este, nos abrirá una consola que nos permitirá crear nuestas fixtures.

Supongamos que ejecutamos en esta consola el siguiente comando:

@david = User.create(:login => 'david', :password => 'pass')

Fixturease nos creará el fichero fixtures/users.yml con la siguiente entrada:

david:
  id: 1
  password: pass
  login: david
  created_at: 2007-01-20 21:40:23.250000 +02:00

Creo que es una buena forma de tener un poco más de control sobre nuestas fixtures y no tener que centrarnos en escribir un fichero yaml a mano.

Ahorrar tiempo con hibernate-tools 1

Posted by david

Una de las pegas que encuentro al trabajar con Hibernate es que me obliga a escribir el script de creación de tablas en base de datos, el fichero de mapeo de estas tablas en Hibernate y las clases Java que actuan como entidad.

Hibernate Tools, aporta un plugin de Eclipse y una serie de tareas para Ant que facilitan un poco la vida del desarrollador. En este artículo vamos a hacer una pequeña introducción a estas tareas para ver como podemos generar diréctamente el script de creación de las tablas y las clases Java a partir de nuestro fichero de mapeo hbm.

Para trabajar con la extensión de Ant no es necesario todo el paquete que nos descargamos. Podemos extraer únicamente el fichero hibernate-tools.jar que se ecuentra dentro del directorio /plugins/org.hibernate.eclipse_3.2.0.beta9a/lib/tools/.

Una vez tenemos la librería en nuestro directorio de trabajo lo primero que tenemos que hacer es definir la tarea hibernatetool dentro de nuestro fichero build.xml:

    <taskdef name="hibernatetool" 
         classname="org.hibernate.tool.ant.HibernateToolTask" 
     classpathref="buildpath" />

En el capítulo dedicado a las tareas de Ant nos indican varias formas para añadir la configuración de Hibernate. Para mí, la más cómoda es decirle donde se encuentra el fichero con la configuración básica y luego añadir los hbm que queremos que utilice:

    <configuration configurationfile="hibernate.cfg.xml">
        <fileset dir="${xml.dir}">
            <include name="**/${hbm.file}.hbm.xml"/>
        </fileset>
    </configuration>

Una vez que ya tenemos configurada nuestra tarea veamos los métodos que tenemos disponibles para exportar los ficheros hbm.

El primero de ellos es hbm2java, el encargado de generar nuestras clases Java. Su uso es muy sencillo:

    <hbm2java/>

El segundo que vamos a ver es hbm2ddl. Este exporter nos genera el sql de creación de las tablas en base de datos. Los atributos más comunes que se le añaden suelen ser el nombre del fichero que quieres que genere, si quieres que este fichero tenga un buen formateado o si deseas que además ejecute el sql directamente contra la base de datos:

    <hbm2ddl outputfilename="hbm2ddl.sql" 
        export="false" create="true" format="true"/>

Con todo esto ya tenemos nuestra nueva tarea creada. Para el que no le apetezca hacer mucho copy/paste puede ver todo el código en el fichero que yo suelo utilizar.

Extender relaciones de ActiveRecord 1

Posted by david

Hay veces que te encuentras pequeños trucos de Rails que muestran en unas pocas líneas de código toda la potencia de este framework.

En este caso James Buck, nos enseña como extender relaciones entre objectos ActiveRecord añadiendoles métodos adicionales.

Con este simple truco podremos escribir código tan limpio como:

mail.inbox.unreaded

de una forma mucho más eficiente de la que algunos estamos acostumbrados.

Ruby y Ejabberd 2

Posted by david

Ejabberd es uno de los servidores jabber más populares que existen y una de sus características más destacable es la posibilidad de autenticar usuarios de forma externa. Con esta opción podremos integrar mensajería jabber en nuestra aplicación sin tener que modificar nuestra base de datos o migrar nuestros usuario ya registrados a la base de datos del servidor.

En la página del proyecto podemos encontrar varios scripts para autenticar usuarios contra distintas bases de datos o servicios de directorio.

En este artículo vamos a ver como configurar el servidor para que autentique a los usuarios a través de un script en Ruby contra una base de datos Mysql.

La configuración del servidor es sencilla, dentro del directorio donde tenemos instalado ejabberd abrimos el archivo "conf/ejabberd.cfg" comentando la línea donde se indica la autenticación interna:

%{auth_method, internal}

y descomentando las líneas donde se indica la autenticación externa y el path al script que vamos a utilizar:

{auth_method, external}.
{extauth_program, "/var/www/apps/trunk/config/ejabberdAuth.rb"}.

A la hora de introducir los parámetros de conexión con nuestra base de datos tenemos varias opciones, pero si estamos desarrollado una aplicación con Ruby on Rails podemos recoger estos datos directamente de nuestra configuración:

environment = 'development'
#PATH ABSOLUTO AL FICHERO DE CONFIGURACION
config_file = '/var/www/apps/trunk/config/database.yml'
cfg = YAML.load_file(config_file)[environment]

Para conectarnos con la base de datos usaremos el módulo DBI para ruby:

host = cfg['host'] || '127.0.0.1'
db_con = "DBI:Mysql:#{cfg['database']}:#{host}"
@db = DBI.connect db_con, cfg['username'] , cfg['password']

Los paquetes que el servidor ejabberd envia y recibe del script tienen un formato determando. Cuando el script recibe un paquete dentro de sus dos primeros bits se encuentra la longitud de la cadena de datos que tenemos que leer. Esta cadena tiene un formato determinado:

operación : usuario : nombre del servidor jabber : contraseña

Por ejemplo:

auth:david:thinkincode.net:david

La respuesta al servidor debe ser 1 en caso de que la operación sea correcta o 0 en caso contrario. Esta respuesta también está empaquetada con los dos primeros bits libres para su longitud.

buffer = String.new
while STDIN.sysread(2, buffer) && buffer.length == 2

  length = buffer.unpack('n')[0]
  operation, username, domain, password = 
      STDIN.sysread(length).split(':')	      
	      
  response = case operation 
      when 'auth'
          auth username, password.chomp
      when 'isuser'              
          isuser username
      else
          0
      end
				    
  STDOUT.syswrite([2, response].pack('nn'))
end

Una vez que hemos obtenido los datos introducidos por el usuario sólo tendremos que validarnos contra nuestra base de datos según la operación que hayamos recibido. El método para autenticar el usuario podría quedar así:

def auth(username, password)
  row = @db.select_one("select password from users"
    " where user_name = ? and activated_at IS NOT NULL", 
    username)
    
  return (1 if row and row['password'] == password) || 0
end

Una vez terminado el script tendríamos que arancar el servidor jabber para que nuestros usuarios puedan conectarse con su cliente favorito usando el usuario y contraseña que han introducido en nuestra aplicación.

Un pequeño truco para ver si el servidor ha arrancado correctamente es ver si tenemos un proceso corriendo con el script que hemos configurado.

Dejo el código completo del script para poder ver el ejemplo más detalladamente.