sexta-feira, 18 de dezembro de 2009

Serializador Java para JSON - Parte I

JSON é uma forma de transferência de dados que está sendo utilizado bastante atualmente. Possui um formato bastante que deixa o conjunto de dados bastante enxuto.

Desenvolvi estas classes baseadas na biblioteca FlexJson: http://flexjson.sourceforge.net/ . Fiz algumas alterações que deixou a biblioteca bem menor. Mas, minha motivação principal foi que eu pudesse utilizar a biblioteca  na versão 1.4 do Java. Ao todo, a biblioteca ficou com apenas quatro classes: ChainedIterator, ChainedSet, JSONSerializer, ObjectVisitor. Vou colocar o código de todas elas, e também um exemplo de uso.

Código da classse ChainedIterator

import java.util.Iterator;
import java.util.Iterator;
import java.util.Set;

public class ChainedIterator implements Iterator {
Iterator[] iterators;
int current = 0;

public ChainedIterator(Set sets) {
Object[] arraySets = sets.toArray();
iterators = new Iterator[sets.size()];
for( int i = 0; i < sets.size(); i++ ) {
iterators[i] = ((Set)arraySets[i]).iterator();
}
}

public boolean hasNext() {
if( iterators[current].hasNext() ) {
return true;
} else {
current++;
return current < iterators.length && iterators[current].hasNext();
}
}

public Object next() {
return iterators[current].next();
}

public void remove() {
iterators[current].remove();
}
}

Código da classe ChainedSet
import java.util.Set;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Collection;
import java.util.TreeSet;

public class ChainedSet implements Set {
Set parent;
Set child;

public ChainedSet(Set parent) {
this.parent = parent;
this.child = new HashSet();
}

public int size() {
return this.child.size() + parent.size();
}

public boolean isEmpty() {
return this.child.isEmpty() && parent.isEmpty();
}

public boolean contains(Object o) {
return child.contains(o) || parent.contains(o);
}

public Iterator iterator() {
Set sets = new TreeSet();
sets.add(child);
sets.add(parent);
return new ChainedIterator(sets);
}

public Object[] toArray() {
Object[] carr = child.toArray();
Object[] parr = parent.toArray();
Object[] combined = new Object[ carr.length + parr.length ];
System.arraycopy( carr, 0, combined, 0, carr.length );
System.arraycopy( parr, 0, combined, carr.length, parr.length );
return combined;
}

public Object[] toArray(Object[] a) {
throw new IllegalStateException( "Not implemeneted" );
}

public boolean add(Object o) {
return child.add( o );
}

public boolean remove(Object o) {
return child.remove( o );
}

public boolean containsAll(Collection c) {
return child.containsAll(c) || parent.containsAll(c);
}

public boolean addAll(Collection c) {
return child.addAll( c );
}

public boolean retainAll(Collection c) {
return child.retainAll( c );
}

public boolean removeAll(Collection c) {
return child.removeAll( c );
}

public void clear() {
child.clear();
}

public Set getParent() {
return parent;
}
}
Código da classe JSONSerializer
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class JSONSerializer {

public final static char[] HEX = "0123456789ABCDEF".toCharArray();

List pathExpressions = new ArrayList();
Map transformations = new HashMap();

public JSONSerializer() {
}

public String serialize( String rootName, Object target ) {
return new ObjectVisitor().visit( rootName, target );
}

public String serialize( Object target ) {
return new ObjectVisitor().visit( target );
}

public String prettyPrint( Object target ) {
return new ObjectVisitor( true ).visit( target );
}

public String prettyPrint( String rootName, Object target ) {
return new ObjectVisitor( true ).visit( rootName, target );
}
}

Código da classe ObjectVisitor
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.text.CharacterIterator;
import java.text.StringCharacterIterator;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.Map;

public class ObjectVisitor {
protected StringBuffer builder;
protected boolean prettyPrint = false;
private int amount = 0;
private boolean insideArray = false;

protected ObjectVisitor() {
builder = new StringBuffer();
}

public ObjectVisitor(boolean prettyPrint) {
this();
this.prettyPrint = prettyPrint;
}

public String visit( Object target ) {
json( target );
return builder.toString();
}

public String visit( String rootName, Object target ) {
beginObject();
string(rootName);
add(':');
json( target );
endObject();
return builder.toString();
}

private void numberToString(Number n) {
if (n != null && testValidity(n)) {
String s = n.toString();
if (s.indexOf('.') > 0 && s.indexOf('e') < 0 && s.indexOf('E') < 0) {
while (s.endsWith("0")) {
s = s.substring(0, s.length() - 1);
}
if (s.endsWith(".")) {
s = s.substring(0, s.length() - 1);
}
}
add(s);
}else{
add("0");
}
}

private boolean testValidity(Object o) {
if (o != null) {
if (o instanceof Double) {
if (((Double)o).isInfinite() || ((Double)o).isNaN()) {
return false;
}
} else if (o instanceof Float) {
if (((Float)o).isInfinite() || ((Float)o).isNaN()) {
return false;
}
}
}
return true;
}


private void json(Object object) {
if (object == null) add("null");
else if (object instanceof Class)
string( ((Class)object).getName() );
else if (object instanceof Boolean)
bool( ((Boolean) object) );
else if (object instanceof String)
string(object);
else if (object instanceof Character)
string(object);
else if (object instanceof Number)
numberToString((Number) object);
else if (object instanceof Map)
map( (Map)object);
else if (object.getClass().isArray())
array( object );
else if( object instanceof Date)
date( (Date)object );
else if (object instanceof Collection)
array(((Collection) object).iterator() );
else
bean( object );
}

private void map(Map map) {
beginObject();
Iterator it = map.keySet().iterator();
boolean firstField = true;
while (it.hasNext()) {
Object key = it.next();
int len = builder.length();
add( key, map.get(key), firstField );
if( len < builder.length() ) {
firstField = false;
}
}
endObject();
}

private void array(Iterator it) {
beginArray();
while (it.hasNext()) {
if( prettyPrint ) {
addNewline();
}
addArrayElement( it.next(), it.hasNext() );
}
endArray();
}

private void array(Object object) {
beginArray();
int length = Array.getLength(object);
for (int i = 0; i < length; ++i) {
if( prettyPrint ) {
addNewline();
}
addArrayElement( Array.get(object, i), i < length - 1 );
}
endArray();
}

private void addArrayElement(Object object, boolean isLast ) {
int len = builder.length();
json( object );
if( len < builder.length() ) { // make sure we at least added an element.
if ( isLast ) add(',');
}
}

private void bool(Boolean b) {
add( b.booleanValue() ? "true" : "false" );
}

private void string(Object obj) {
add('"');
CharacterIterator it = new StringCharacterIterator(obj.toString());
for (char c = it.first(); c != CharacterIterator.DONE; c = it.next()) {
if (c == '"') add("\\\"");
else if (c == '\\') add("\\\\");
else if (c == '\b') add("\\b");
else if (c == '\f') add("\\f");
else if (c == '\n') add("\\n");
else if (c == '\r') add("\\r");
else if (c == '\t') add("\\t");
else if (Character.isISOControl(c)) {
unicode(c);
} else {
add(c);
}
}
add('"');
}

private void date(Date date) {
builder.append( date.getTime());
}

private ChainedSet visits = new ChainedSet( Collections.EMPTY_SET );

protected void bean(Object bean) {
if( !visits.contains( bean ) ) {
visits = new ChainedSet( visits );
visits.add( bean );
beginObject();
try {
Class klass = bean.getClass();

Method[] methods = klass.getMethods();
boolean firstField = true;
for (int i = 0; i < methods.length; i += 1) {
Method method = methods[i];
if (Modifier.isPublic(method.getModifiers())) {
String name = method.getName();
String key = "";
boolean invoke = false;
if (name.startsWith("get")) {
invoke = true;
key = name.substring(3);
} else if (name.startsWith("is")) {
key = name.substring(2);
invoke = true;
}
if (key.length() > 0){
key = String.valueOf(key.charAt(0)).toLowerCase() + key.substring(1);
}

if (invoke) {
Object value = method.invoke(bean, (Object[])null);

if( !visits.contains( value ) ) {
add(key, value, firstField);
firstField = false;
}
}
}
}
} catch( Exception e ) {

}
endObject();
visits = (ChainedSet) visits.getParent();
}
}

protected boolean addComma(boolean firstField) {
if ( !firstField ) {
add(',');
} else {
firstField = false;
}
return firstField;
}

protected void beginObject() {
if( prettyPrint ) {
if( insideArray ) {
indent( amount );
}
amount += 4;
}
add( '{' );
}

protected void endObject() {
if( prettyPrint ) {
addNewline();
amount -= 4;
indent( amount );
}
add( '}' );
}

private void beginArray() {
if( prettyPrint ) {
amount += 4;
insideArray = true;
}
add('[');
}

private void endArray() {
if( prettyPrint ) {
addNewline();
amount -= 4;
insideArray = false;
indent( amount );
}
add(']');
}

protected void add( char c ) {
builder.append( c );
}

private void indent(int amount) {
for( int i = 0; i < amount; i++ ) {
builder.append( " " );
}
}

private void addNewline() {
builder.append("\n");
}

protected void add( Object value ) {
builder.append( value );
}

protected void add(Object key, Object value, boolean prependComma) {
int start = builder.length();
addComma( prependComma );
addAttribute( key );

int len = builder.length();
json( value );
if( len == builder.length() ) {
builder.delete( start, len ); // erase the attribute key we didn't output anything.
}
}

private void addAttribute(Object key) {
if( prettyPrint ) {
addNewline();
indent( amount );
}
builder.append("\"");
builder.append( key );
builder.append( "\"" );
builder.append( ":" );
if( prettyPrint ) {
builder.append(" ");
}
}

private void unicode(char c) {
add("\\u");
int n = c;
for (int i = 0; i < 4; ++i) {
int digit = (n & 0xf000) >> 12;
add(JSONSerializer.HEX[digit]);
n <<= 4;
}
}
}


Serializador Java para JSON - Parte II

Na Parte I do post sobre JSON, postei o código da biblioteca. Nesta parte dois, está um exemplo de uso, que é formado por uma classe principal (TesteJson) e pelas classes Person e PhoneNumber para ilustrar o uso de objetos para a escrita do código JSON.

Código da classe PhoneNumber


public class PhoneNumber {
private int code;
private String number;

public PhoneNumber() {
}

public PhoneNumber(int code, String number) {
this.code = code;
this.number = number;
}
//Métodos GET e SET.....
}



Código da classe Person


import java.util.Date;
import java.util.List;

public class Person {
private String firstName;
private String lastname;
private int idade;
private boolean casado;
private Boolean solteiro;
private Long altura;
private Integer numero;
private PhoneNumber phone;
private PhoneNumber fax;
private Date data;
private List phones;
private PhoneNumber[] arrayPhones;

public Person() {}

public Person(String fn, String ln) {
this.firstName = fn;
this.lastname = ln;
}

// Métodos GET e SET........
}




Código da classe TesteJson


import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import json.serializer.JSONSerializer;

public class TesteJson {
public static void main(String[] args) {
Person joe = new Person("Joe", "Wayne");
PhoneNumber phoneJoe = new PhoneNumber(321, "5565-987");
PhoneNumber fax = new PhoneNumber(123, "9999-999");
PhoneNumber[] arrayPhones = new PhoneNumber[2];
arrayPhones[0] = phoneJoe;
arrayPhones[0] = fax;
List phones = new ArrayList();
phones.add(phoneJoe);
phones.add(fax);

joe.setPhone(phoneJoe);
joe.setFax(fax);
joe.setData(Calendar.getInstance().getTime());
joe.setIdade(12);
joe.setAltura(new Long(120));
joe.setArrayPhones(arrayPhones);
joe.setPhones(phones);
joe.setAltura(new Long(120));
joe.setCasado(true);
joe.setSolteiro(new Boolean(false));
joe.setNumero(new Integer(53));

Person moe = new Person("Bart", "Simpson");
PhoneNumber phone = new PhoneNumber(321, "5565-987");
moe.setPhone(phone);
moe.setFax(new PhoneNumber(321, "9999-999"));
moe.setData(Calendar.getInstance().getTime());

List collection = new ArrayList();
collection.add(joe);
collection.add(moe);

JSONSerializer serial = new JSONSerializer();
String strJoe = serial.serialize(joe);
System.out.println(strJoe);

serial = new JSONSerializer();
strJoe = serial.serialize(collection);
System.out.println(strJoe);
}
}


quinta-feira, 17 de dezembro de 2009

Lookup de um EJB

A seguinte classe é um exemplo básico de como fazer um lookup em um EJB.

O servidor utilizado foi o JBoss, versão 4.0.

É importante que o EJB possua uma interface remota para que o lookup funcione.

import javax.naming.InitialContext;

import br.com.ebiz.funasawf.entity.Usuario;
import br.com.ebiz.funasawf.service.cadastro.UsuarioLocal;

public class LookupEJB {
public static void main(String[] args) {
Hashtable environment = new Hashtable();
environment.put("java.naming.factory.initial","org.jnp.interfaces.NamingContextFactory");
environment.put("java.naming.factory.url.pkgs","org.jboss.naming:org.jnp.interfaces");
environment.put("java.naming.provider.url", "localhost:1099");
// remote machine IP
InitialContext context;
try {
context = new InitialContext(environment);
UsuarioLocal pl = (UsuarioLocal) context.lookup("funasawf/UsuarioWFBean/remote");
Usuario user = pl.findById(39L);
System.out.println(user.getNmeLogin());
} catch (Exception e) {}
}
}

Esse tipo de código é bastante útil para realizar testes na aplicação.

Para poder realizar os testes, você vai precisar das bibliotecas do JBoss: server e client. Além disso, você vai precisar referenciar as classes da sua aplicação, EntityBeans e SessionBeans, como no exemplo, Usuario e UsuarioLocal.