Ir al contenido principal

This is my blog, more about me at marianoguerra.github.io

🦋 @marianoguerra.org 🐘 @marianoguerra@hachyderm.io 🐦 @warianoguerra

Frankfurt

Después de mas de un año de haber vuelto de mi viaje a Alemania decidí organizar las fotos, son un montón, tengo muchas duplicadas y carpetas de todos mis amigos, hasta ahora lo único que logre fue unificar las fotos de Frankfurt, que es el lugar al que llegamos en el vuelo y estuvimos 2 días antes de partir para nuestros lugares definitivos.
Acá pongo una selección de las mejores fotos de Frankfurt.



En el parque botánico




El banco central europeo


pato en el hostel


Con tuza y lu en un mirador de un puente


Una plaza con muchos edificios antiguos




La costanera del Main




La salida del subte en la "ciudad universitaria" de Frankfurt

problemas de pynotify con threads y gtk

agregue soporte para notificaciones en emesene 2, y de pronto la gui se empezó a congelar (la aplicación no porque se podía ver el log del protocolo que seguía pasando). Me di cuenta muy de suerte que el problema eran las notificaciones (había metido muchos otros cambios) ya que en debian no tenia python-notify instalado y andaba sin problemas y en ubuntu se congelaba, instale python-notify y se empezó a congelar de nuevo.

comente las lineas y andaba, por lo que era eso.. busque en google y vi un parche que en lugar de:

gtk.gdk.threads_init()

hacia:

gobject.threads_init()
gtk.gdk.threads_init()

probe con eso y salio con fritas.

este post es por si algún día les pasa algo parecido no tengan que pelear un buen rato para ver donde estaba el problema.

Usos curiosos de cosas

Termine de leer los otros días "revelion en la granja" y no se me ocurría que libro comprar asi que decidí pedirle a google que me recomiende usando google sets http://labs.google.com/sets.

puse los que ya leí con temática similar al próximo que quiero leer a ver como me ampliaba el set, los que puse fueron:

* the time machine
* 1984
* animal farm
* blade runner
* brave new world
* 2001 a space odyssey

y me recomendo

* catch 22
* fahrenheit 451
* neuromancer
* harry potter
* atlas shrugged
* hamlet

de los cuales decidí leer fahrenheit 451 y quizás después neuromancer, el que no conocía de ahí era atlas shrugged que según dice wikipedia "Esta novela fue la última obra de ficción de Ayn Rand, antes de dedicarse sólo a la Filosofía. Con 645.000 palabras, y entre 1000 y 1200 páginas según ediciones, es también una de las novelas más largas jamás escritas." asi que la dejare para cuando tenga mas tiempo :P

Presentacion javascript

Esta es una presentación que hice para el ciclo coffee and cookies de la utn de cordoba, fue hecho un poco a las corridas asi que no esperen algo muy pulido :D

Reproducir audio en python multiplataforma

una receta de como reproducir un archivo de audio con python en linux, mac y windows.

si encuentran un bug, por favor avisen en los comentarios, solo lo probe en linux :)


import os

def is_on_path(fname):
"""
returns True if fname is the name of an executable on path (*nix only)
"""
for p in os.environ['PATH'].split(os.pathsep):
if os.path.isfile(os.path.join(p, fname)):
return True

return False

def dummy_play(path):
"""
dummy method used when no method is available
"""
print "can't play", path

if os.name == 'nt':
import winsound

def play(path):
winsound.PlaySound(path, winsound.SND_FILENAME)
elif os.name == 'posix':
try:
import gst

def play(path):
"""
play a sound using gstreamer api
"""
_player = gst.element_factory_make("playbin", "player")
uri = "file://" + os.path.abspath(path)
_player.set_property('uri', uri)
_player.set_state(gst.STATE_PLAYING)
except ImportError:
if is_on_path('play'):
play = lambda path: os.popen4('play ' + path)
elif is_on_path('aplay'):
play = lambda path: os.popen4('aplay ' + path)
else:
play = dummy_play
elif os.name == 'mac':
from AppKit import NSSound
def play(path):
"""
play a sound using mac api
"""
macsound = NSSound.alloc()
macsound.initWithContentsOfFile_byReference_(soundPath, True)
macsound.play()
else:
play = dummy_play


para usarlo

import play_sound
play_sound.play('path/al/archivo.wav')

para el caso de gstreamer en linux y el de mac, hace falta un mainloop o algo que mantenga la aplicacion abierta para que reproduzca, con popen no hace falta, en windows no se si bloquea o no.

Como consumuir Webservice REST JSON en Javascript

este post es continuación de este: http://marianoguerra.blogspot.com/2009/03/serializardesserializar-objetos-java.html

ahora vamos a hacer un modulo en javascript para usar la API REST JSON desde una pagina web (lo que normalmente se llama Web 2.0 o AJAX solo que la X de XML la cambiamos por la J de JSON asi que queda AJAJ).

el primer problema que debemos superar es la regla de "same origin" de javascript por la cual solo podemos hacer requests asincronicos con HttpRequest solo a la misma URL de la cual se descargo el script, y como nuestra aplicación se va a distribuir en un jar, nuestro js y html tiene que estar en ese jar y lo tiene que servir la misma aplicación, lo que significa que si le pido 0.0.0.0:9999/pagina.html la aplicación tiene que buscar en el jar el archivo en algun lugar y devolverlo. Si el archivo viene de otro lado nuestro request va a fallar.

Para solucionar esto la unica forma que encontre fue escribir un handler a mano para devolver archivos estaticos.

/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/

package addressbook;

import java.io.IOException;
import java.net.MalformedURLException;
import org.mortbay.jetty.handler.ResourceHandler;
import org.mortbay.resource.Resource;

/**
*
* @author mariano
*/
public class ContentHandler extends ResourceHandler{
@Override
public Resource getResource(String path) throws MalformedURLException
{
System.out.println(path);
try
{
System.out.println(getResourceBase().replaceAll("file:", "") + path);
return Resource.newResource(this.getClass().getResource(getResourceBase().replaceAll("file:", "") + path));
}
catch(IOException ex)
{
return null;
}
}
}


el archivo Main.java se modifica para agregar este handler a la lista de handlers, quedaria asi:

/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/

package addressbook;

import com.sun.jersey.spi.container.servlet.ServletContainer;
import org.mortbay.jetty.Handler;
import org.mortbay.jetty.handler.DefaultHandler;
import org.mortbay.jetty.handler.HandlerList;
import org.mortbay.jetty.servlet.Context;
import org.mortbay.jetty.servlet.ServletHolder;

/**
*
* @author mariano
*/
public class Main {

/**
* @param args the command line arguments
*/
public static void main(String[] args) throws Exception{
org.mortbay.jetty.Server server;
ServletHolder holder;
Context contextWS;

holder = new ServletHolder(ServletContainer.class);

holder.setInitParameter("com.sun.jersey.config.property.packages",
"addressbook.ws");

server = new org.mortbay.jetty.Server(9999);

contextWS = new Context(server, "/ws", Context.SESSIONS);
contextWS.addServlet(holder, "/*");
ContentHandler resource_handler = new ContentHandler();
resource_handler.setResourceBase("/resources");
HandlerList handlers = new HandlerList();
handlers.setHandlers(new Handler[] { resource_handler, contextWS, new DefaultHandler()});
server.setHandler(handlers);
addressbook.ws.AddressBook.fill();
server.start();
}

}


ahora creamos un paquete llamado resources en la base del proyecto en donde vamos a poner nuestros archivos estaticos.

para poder hacer nuestra pagina über AJAX vamos a necesitar dos librerias, jquery y JSON.js, la primera nos ayuda en muchas cosas, siendo una de ellas la de hacer request HTTP que funcione en todos los browsers, la segunda nos sirve para serializar/deserializar objetos javascript.

las urls son las siguientes:

http://jquery.com/

http://www.json.org/json2.js


ponemos la libreria jquery-version.js y json2.js que descargamos en el paquete resources.js, creamos un archivo addressbook.js en el mismo paquete.

tambien creamos un archivo addressbook.html en el paquete resources, osea que la estructura quedaria asi:


/resources
/resources/addressbook.html
/resources/js
/resources/js/addressbook.js
/resources/js/jquery-1.3.1.min.js
/resources/js/json2.js

en addressbook.js vamos a crear los metodos de javascript que nos permitan interactuar con el servidor, asi como las clases que debemos enviar serializadas.

ahora pongo el contenido de los archivos.

resources/addressbook.html


<html>
<head>
<title>Address Book</title>
<link rel="stylesheet" type="text/css" href="css/style.css" />

<script src="js/jquery-1.3.1.min.js" type="text/javascript"></script>
<script src="js/json2.js" type="text/javascript"></script>
<script src="js/addressbook.js" type="text/javascript"></script>
<script type="text/javascript">

function isArray(obj)
{
if(typeof obj == 'object')
{
var criterion = obj.constructor.toString().match(/array/i);
return (criterion != null);
}

return false;
}

function search()
{
var query = $('#search').val();
AddressBook.search("all", query, onSearchSucceed, onSearchFailed);
}

function onSearchSucceed(response)
{
showResults(response);
}

function showResults(response)
{
var results = $('.results');
results.html("");
entries = JSON.parse(response);

if(entries == null)
{
return;
}

if(!isArray(entries["entry"]))
{
var item = entries["entry"];
results.append(item.firstName + " " + item.lastName + '<br/>');
return;
}

for(var i in entries["entry"])
{
var item = entries["entry"][i];
results.append(item.firstName + " " + item.lastName + '<br/>');
}
}

function onSearchFailed(response)
{
alert("La busqueda fallo");
}

function getAll()
{
AddressBook.getAll(onGetAllSucceeded, onGetAllFailed);
}

function onGetAllSucceeded(response)
{
showResults(response);
}

function onGetAllFailed(response)
{
alert("La busqueda fallo");
}

function create()
{
var firstName = $('#firstName').val();
var lastName = $('#lastName').val();
var address = $('#address').val();
var workPhone = $('#workPhone').val();
var homePhone = $('#homePhone').val();
var cellPhone = $('#cellPhone').val();

var entry = new AddressBook.AddressBookEntry(firstName, lastName, address, homePhone, workPhone, cellPhone);
AddressBook.create(entry, onCreateSucceeded, onCreateFailed);
}

function onCreateSucceeded(response)
{
alert("entrada añadida con exito");
}

function onCreateFailed(response)
{
alert("error añadiendo entrada");
}
</script>
</head>

<body>
<h1>Address Book</h1>

<table>
<tr><td>Nombre</td><td><input type="text" id="firstName"></td></tr>
<tr><td>Apellido</td><td><input type="text" id="lastName"></td></tr>
<tr><td>direccion</td><td><input type="text" id="address"></td></tr>
<tr><td>Telefono Trabajo</td><td><input type="text" id="workPhone"></td></tr>
<tr><td>Telefono Casa</td><td><input type="text" id="homePhone"></td></tr>
<tr><td>Telefono Celular</td><td><input type="text" id="cellPhone"></td></tr>
<tr><td></td><td><input type="button" value="Crear" onclick="create();"></td></tr>
</table>

<input type="text" id="search" />
<input type="button" value="Search" onclick="search();" />
<input type="button" value="Get All" onclick="getAll();" />
<div class="results">
</div>
</body>
</html>


resources/js/addressbook.js

// AddressBook namespace
var AddressBook = new Object();

AddressBook.AddressBookEntry = function(firstName, lastName, address, homePhone, workPhone, cellPhone) {
this.firstName = firstName;
this.lastName = lastName;
this.address = address;
this.homePhone = homePhone;
this.workPhone = workPhone;
this.cellPhone = cellPhone;
}

// serializa a JSON
AddressBook.AddressBookEntry.prototype.serialize = function()
{
return JSON.stringify(this);
}

AddressBook.AddressBook = function(entries)
{
this.entries = entries || [];
}

AddressBook.AddressBook.prototype.serialize = function()
{
return JSON.stringify(this);
}

// devuelve una funcion que hace un request REST a path, usando method, el
// metodo devuelto recibe como primer parametro una entidad que es enviada
// en el *cuerpo* del request, por ello este metodo es util para PUT y POST
AddressBook.buildCreateUpdate = function(method, path)
{
return function(entity, success_cb, error_cb)
{
$.ajax({url: path, type: method, dataType: "text", contentType: "application/json",
data: entity.serialize(),
success: success_cb, error: error_cb});
}
}

// devuelve una funcion que hace un request REST a path, usando method, el
// metodo devuelto recibe como primer un string que es agregado al path
// por ello este metodo es util para GET y DELETE
AddressBook.buildGetDelete = function(method, path)
{
return function(id, success_cb, error_cb)
{
$.ajax({url: path + id, type: method, dataType: "text",
success: success_cb, error: error_cb});
}
}

AddressBook.getAll = function(success_cb, error_cb)
{
var request = AddressBook.buildGetDelete("GET", "/ws/book/json/all");
request("", success_cb, error_cb);
}


AddressBook.search = function(type, query, success_cb, error_cb)
{
var request = AddressBook.buildGetDelete("GET", "/ws/book/json/search");
request("/" + type + "/" + query, success_cb, error_cb);
}

AddressBook.create = AddressBook.buildCreateUpdate("PUT", "/ws/book/");


tambien hice algunos cambios en addressbook.ws.AddressBook, aca esta la version actual

/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/

package addressbook.ws;

import addressbook.model.AddressBookEntry;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;

/**
*
* @author mariano
*/
@Path("book")
public class AddressBook {
private static addressbook.model.AddressBook addressBook;

public static addressbook.model.AddressBook search(String type, String query)
{
if(type.equals("firstName"))
{
return new addressbook.model.AddressBook(addressBook.searchByFirstName(query));
}
else if(type.equals("lastName"))
{
return new addressbook.model.AddressBook(addressBook.searchByLastName(query));
}
else
{
return new addressbook.model.AddressBook(addressBook.search(query));
}
}

public static addressbook.model.AddressBook getAddressBook()
{
if(addressBook == null)
addressBook = new addressbook.model.AddressBook();

return addressBook;
}

public static void fill()
{
getAddressBook();
addressBook.addEntry(new AddressBookEntry("Bob", "Esponja", "Fondo de Bikini 1", "123", "321", "5551"));
addressBook.addEntry(new AddressBookEntry("Gary", "Caracol", "Fondo de Bikini 2", "234", "432", "5552"));
addressBook.addEntry(new AddressBookEntry("Arenita", "Ardilla", "Fondo de Bikini 3", "345", "543", "5553"));
addressBook.addEntry(new AddressBookEntry("Patricio", "Estrella", "Fondo de Bikini 4", "456", "654", "5554"));
addressBook.addEntry(new AddressBookEntry("Calamardo", "Calamar", "Fondo de Bikini 5", "567", "765", "5555"));
}

@GET
@Path("xml/all")
@Produces("application/xml")
public static addressbook.model.AddressBook getAllXml()
{
return addressBook;
}

@GET
@Path("json/all")
@Produces("application/json")
public static addressbook.model.AddressBook getAllJson()
{
return addressBook;
}

@GET
@Path("xml/search/{type}/{query}")
@Produces("application/xml")
public static addressbook.model.AddressBook searchXml(@PathParam("type") String type, @PathParam("query") String query)
{
return search(type, query);
}

@GET
@Path("json/search/{type}/{query}")
@Produces("application/json")
public static addressbook.model.AddressBook searchJson(@PathParam("type") String type, @PathParam("query") String query)
{
return search(type, query);
}

@PUT
@Consumes({"application/json", "application/xml"})
public static void add(AddressBookEntry entry)
{
addressBook.addEntry(entry);
}
}

con este ejemplo tenemos una aplicación que expone una API REST y un cliente en javascript/html que lo consume y utiliza, expandiendo la cantidad de entidades en la API podemos escalar a una aplicacion completa.

Si ando con ganas en el proximo post voy a explicar como usar un ORM (object relational mapper) para almacenar el contenido de la entidad en una base de datos.

el codigo completo de este ejemplo lo voy a colgar en github en esta direccion http://github.com/marianoguerra/examples/tree/master, ahora a aprender a usar git :)

Serializar/desserializar objetos java a/de XML/JSON

este post es la segunda parte de este post: http://marianoguerra.blogspot.com/2009/03/webservices-rest-en-java-con-jetty-y.html


Para tener webservices REST como la gente necesitamos poder transformar nuestros objetos a una representacion que pueda ser entendida por muchos lenguajes y de vuelta de esa representacion a objetos java, para ello vamos a crear los objetos de nuetro modelo y los vamos anotar para poder serializar/desserializar a XML y JSON, yo particularmente prefiero JSON porque es mas simple, legible y interactua muy bien con javascript, que suele ser el otro extremo de cualquier aplicacion web, pero no importa ya que de la forma que voy a mostrar nos permite hacer los dos por el precio de uno.

Para ello creamos primero el paquete model dentro de nuestro proyecto, en mi caso addressbook.model y adentro creamos dos clases, AddressBook y AddressBookEntry

para poder usar las anotaciones de serializacion agregamos los siguientes jars

jaxb-api-2.1.jar
jaxb-impl-2.1.jar

para poder serializar a JSON agregamos el siguiente jar

jettison-1.0.1.jar

para buscar versiones mas nuevas podes ir a http://download.java.net/maven/1/javax.xml.bind/jars/ y http://repository.codehaus.org/org/codehaus/jettison/jettison/ respectivamente.

ahora creamos la clase AddressBookEntry que simplemente va a contener los siguientes atributos privados, todos strings:

  • firstName
  • lastName
  • address
  • homePhone
  • workPhone
  • cellPhone
creamos los getters/setters y dos constructores, uno sin argumentos que inicializa los valores por default y uno con todos los parametros.

ahora llega la hora de decorar la clase para poder serializarla, el resultado es el siguiente:


/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/

package addressbook.model;

import java.io.Serializable;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;

/**
*
* @author mariano
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name="addressBookEntity")
public class AddressBookEntry implements Serializable{
private String firstName;
private String lastName;
private String address;
private String homePhone;
private String workPhone;
private String cellPhone;

public AddressBookEntry() {
}

public AddressBookEntry(String firstName, String LastName, String address, String homePhone, String workPhone, String cellPhone) {
this.firstName = firstName;
this.lastName = LastName;
this.address = address;
this.homePhone = homePhone;
this.workPhone = workPhone;
this.cellPhone = cellPhone;
}

public String getLastName() {
return lastName;
}

public void setLastName(String LastName) {
this.lastName = LastName;
}

public String getAddress() {
return address;
}

public void setAddress(String address) {
this.address = address;
}

public String getCellPhone() {
return cellPhone;
}

public void setCellPhone(String cellPhone) {
this.cellPhone = cellPhone;
}

public String getFirstName() {
return firstName;
}

public void setFirstName(String firstName) {
this.firstName = firstName;
}

public String getHomePhone() {
return homePhone;
}

public void setHomePhone(String homePhone) {
this.homePhone = homePhone;
}

public String getWorkPhone() {
return workPhone;
}

public void setWorkPhone(String workPhone) {
this.workPhone = workPhone;
}


}


si se fijan hay dos anotaciones nomas, uno que le dice el nombre de la entidad cuando la serialize y el otro le dice si debe buscar las anotaciones de los atributos en la definicion de los atributos o en los getters/setters.

programamos la clase que contiene muchos AddressBookEntry y la decoramos para poder serializarla:

/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/

package addressbook.model;

import java.util.Vector;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

/**
*
* @author mariano
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name="addressBook")
public class AddressBook {
@XmlElement(name="entry")
private Vector<AddressBookEntry> entries;

public AddressBook() {
entries = new Vector<AddressBookEntry>();
}

public AddressBook(Vector<AddressBookEntry> entries) {
this.entries = entries;
}

public Vector<AddressBookEntry> getEntries() {
return entries;
}

public void setEntries(Vector<AddressBookEntry> entries) {
this.entries = entries;
}

public void addEntry(AddressBookEntry entry)
{
entries.add(entry);
}

public boolean removeEntry(AddressBookEntry entry)
{
return entries.remove(entry);
}

public Vector<AddressBookEntry> search(String query)
{
Vector<AddressBookEntry> result = new Vector<AddressBookEntry>();

result.addAll(searchByFirstName(query));
result.addAll(searchByLastName(query));
return result;
}

public Vector<AddressBookEntry> searchByFirstName(String query)
{
Vector<AddressBookEntry> result = new Vector<AddressBookEntry>();
for(AddressBookEntry entry: entries)
{
if(entry.getFirstName().indexOf(query) != -1)
{
result.add(entry);
}
}

return result;
}

public Vector<AddressBookEntry> searchByLastName(String query)
{
Vector<AddressBookEntry> result = new Vector<AddressBookEntry>();
for(AddressBookEntry entry: entries)
{
if(entry.getLastName().indexOf(query) != -1)
{
result.add(entry);
}
}

return result;
}
}


Lo unico que cambia con respecto al anterior es que a la coleccion le digo cual es el tag de cada elemento interno de la coleccion.

con este decorador

    @XmlElement(name="entry")

le agregue algunos metodos de busqueda que nos van a servir mientras no tengamos persistencia.

por ultimo modifique la clase que expone el webservice para que genere json y xml y cree un metodo temporal para llenar con datos el addressbook (hasta que haya persistencia).

aca esta el codigo de addressbook.ws.AddressBook:

/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/

package addressbook.ws;

import addressbook.model.AddressBookEntry;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;

/**
*
* @author mariano
*/
@Path("book")
public class AddressBook {
private static addressbook.model.AddressBook addressBook;

public static addressbook.model.AddressBook search(String type, String query)
{
if(type.equals("firstName"))
{
return new addressbook.model.AddressBook(addressBook.searchByFirstName(query));
}
else if(type.equals("lastName"))
{
return new addressbook.model.AddressBook(addressBook.searchByLastName(query));
}
else
{
return new addressbook.model.AddressBook(addressBook.search(query));
}
}

public static addressbook.model.AddressBook getAddressBook()
{
if(addressBook == null)
addressBook = new addressbook.model.AddressBook();

return addressBook;
}

public static void fill()
{
getAddressBook();
addressBook.addEntry(new AddressBookEntry("Bob", "Esponja", "Fondo de Bikini 1", "123", "321", "5551"));
addressBook.addEntry(new AddressBookEntry("Gary", "Caracol", "Fondo de Bikini 2", "234", "432", "5552"));
addressBook.addEntry(new AddressBookEntry("Arenita", "Ardilla", "Fondo de Bikini 3", "345", "543", "5553"));
addressBook.addEntry(new AddressBookEntry("Patricio", "Estrella", "Fondo de Bikini 4", "456", "654", "5554"));
addressBook.addEntry(new AddressBookEntry("Calamardo", "Calamar", "Fondo de Bikini 5", "567", "765", "5555"));
}

@GET
@Path("xml/all")
@Produces("application/xml")
public static addressbook.model.AddressBook getAllXml()
{
return addressBook;
}

@GET
@Path("json/all")
@Produces("application/json")
public static addressbook.model.AddressBook getAllJson()
{
return addressBook;
}

@GET
@Path("xml/search/{type}/{query}")
@Produces("application/xml")
public static addressbook.model.AddressBook searchXml(@PathParam("type") String type, @PathParam("query") String query)
{
return search(type, query);
}

@GET
@Path("json/search/{type}/{query}")
@Produces("application/json")
public static addressbook.model.AddressBook searchJson(@PathParam("type") String type, @PathParam("query") String query)
{
return search(type, query);
}
}


en el main agregue una linea antes de server.start(); en la que llamo a addressbook.ws.AddressBook.fill();

ahora con todo eso puesto, si corremos el ejemplo vamos a obtener resultados serializados, ejemplos de urls para probar los gets:

para obtener todos en xml:

http://0.0.0.0:9999/ws/book/xml/all

para obtener todos en json:

http://0.0.0.0:9999/ws/book/json/all

para buscar los que contienen en el nombre "ob" en xml:

http://0.0.0.0:9999/ws/book/xml/search/firstName/ob/

para buscar los que contienen en el apellido "a" en json:

http://0.0.0.0:9999/ws/book/json/search/lastName/a/

para buscar los que contienen en el nombre o apellido "e" en xml:

http://0.0.0.0:9999/ws/book/xml/search/all/e/

supongo que van entendiendo ;)

bueno, eso es todo por ahora en la proxima como escribir un modulo para javascript para consumir los webservices json y transformarlos en objetos y tambien como "dar de alta" nuevos AddressBookEntry.

Webservices REST en Java con Jetty y Jersey

En este post voy a explicar como hacer una aplicacion Java SE que exponga una API REST tanto JSON como XML.

primero empezamos creando un proyecto comun en netbeans, le ponemos el nombre que se nos ocurra, te recomiendo addressbook asi no tenes que cambiar el codigo :D.

despues tenemos que agregar todos los jars que vamos a usar en el proyecto.

aca doy una lista de las ultimas versiones, pero pueden quedar desactualizadas rapido, asi que fijense si no hay mas nuevas.

Primero que todo necesitamos un servidor web embebido, para ello vamos a usar jetty, el cual bajamos de aca: http://dist.codehaus.org/jetty/ yo baje este http://dist.codehaus.org/jetty/jetty-6.1.15/jetty-6.1.15.zip al descomprimir el zip, en la carpeta lib estan los jars que hacen falta

Tambien necesitamos una libraria para poder exponer nuestros webservices REST, para ello vamos a usar jersey, yo baje el bundle (contiene todos los jars adentro que requiere) de aca http://download.java.net/maven/2/com/sun/jersey/jersey-bundle/, particularmente yo baje la version 1.0.2 de aca http://download.java.net/maven/2/com/sun/jersey/jersey-bundle/1.0.2/jersey-bundle-1.0.2.jar

el jar asm lo podemos descargar de aca http://maven.objectweb.org/maven2/asm/asm/ yo baje la version 3.1 de aca http://maven.objectweb.org/maven2/asm/asm/3.1/asm-3.1.jar

el jar jsr311 se encuentra en http://download.java.net/maven/2/javax/ws/rs/jsr311-api/ yo baje http://download.java.net/maven/2/javax/ws/rs/jsr311-api/1.0/jsr311-api-1.0.jar

Para empezar agregamos los siguientes jar al proyecto:

jersey-bundle-1.0.2.jar
jetty-util-6.1.15.jar
servlet-api-2.5-20081211.jar
jetty-6.1.15.jar
jsr311-api-1.0.jar
asm-3.1.jar

Creamos un archivo main con el siguiente contenido

/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/

package addressbook;

import com.sun.jersey.spi.container.servlet.ServletContainer;
import org.mortbay.jetty.Handler;
import org.mortbay.jetty.handler.DefaultHandler;
import org.mortbay.jetty.handler.HandlerList;
import org.mortbay.jetty.servlet.Context;
import org.mortbay.jetty.servlet.ServletHolder;

/**
*
* @author mariano
*/
public class Main {

/**
* @param args the command line arguments
*/
public static void main(String[] args) throws Exception{
org.mortbay.jetty.Server server;
ServletHolder holder;
Context contextWS;

holder = new ServletHolder(ServletContainer.class);

holder.setInitParameter("com.sun.jersey.config.property.packages",
"addressbook.ws");

server = new org.mortbay.jetty.Server(9999);

contextWS = new Context(server, "/ws", Context.SESSIONS);
contextWS.addServlet(holder, "/*");
HandlerList handlers = new HandlerList();
handlers.setHandlers(new Handler[] { contextWS, new DefaultHandler()});
server.setHandler(handlers);

server.start();
}

}



lo que hace es inicializar el server en el puerto 9999 y pasarle un handler para los requests que estan bajo el path /ws.

la linea

holder.setInitParameter("com.sun.jersey.config.property.packages", "addressbook.ws");


le dice que busque los manejadores de ws en el paquete addressbook.ws, por lo tanto el siguiente paso es crear el paquete addressbook.ws.

Por lo que veran el ejemplo va a ser un libro de direcciones o cosas por el estilo.

despues de crear el paquete creamos una clase adentro que llamamos AddressBook

el contenido es el siguiente:

/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/

package addressbook.ws;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.QueryParam;

/**
*
* @author mariano
*/
@Path("book")
public class AddressBook {

@GET
@Path("all")
public static String getAll()
{
return "all";
}

@GET
@Path("search/{type}/{query}")
public static String getAll(@PathParam("type") String type, @PathParam("query") String query)
{
return type + " " + query;
}
}



El codigo es bastante simple, en la clase le decimos cual es el path base de esa clase, y en cada metodo le decimos que tipo de request responde (en lenguaje REST tenemos GET, POST, PUT y DELETE) y tambien podemos especificarle parametros los cuales van entre llaves en @Path y tambien en el metodo debemos anotar cual parametro del path se asigna a que parametro del metodo.

Por ahora los metodos devuelven cosas simples, en otro post voy a mostrar como serializar/descerializar objetos java de/a XML y JSON

si lo corremos y vamos con el browser a http://0.0.0.0:9999/ws/book/all vamos a ver el texto "all", si vamos a http://0.0.0.0:9999/ws/book/search/name/bob vamos a ver "name bob"