Gastaldi's Blog
Mais um blog sobre Java …
Serviço REST para conversão de moeda
Há algum tempo atrás, quando ainda estava conhecendo o JBoss RestEasy, encontrei um serviço que realiza a conversão de moedas baseado em um script CGI. O Site é este aqui : http://www.xe.com
Pois bem, o Java possui a classe Currency, e a saída do site é um trecho em HTML, logo, basta criarmos um MessageBodyReader para ler o conteúdo do trecho HTML e extrair as partes necessárias.
Comecei com a criação da interface REST:
package com.george.rest.currency;
import java.math.BigDecimal;
import java.util.Currency;
import javax.ws.rs.GET;
import javax.ws.rs.QueryParam;
/**
* REST interface para recuperar Informações do site XE (http://www.xe.com) em tempo real
* @author george
*
*/
public interface ICurrencyConverter {
public final static String REST_SERVICE_URL = "http://www.xe.com/ucc/convert.cgi";
/**
* Chama um serviço REST e devolve informações do câmbio atual
* @param amount
* @param from
* @param to
* @return
*/
@GET
public abstract CurrencyConversion convert(
@QueryParam("Amount") BigDecimal amount,
@QueryParam("From") Currency from,
@QueryParam("To") Currency to);
}
Para permitir retornar mais informações (data da conversão, quantia, conversão de um valor para outro), decidi encapsular o retorno da chamada em um POJO:
package com.george.rest.currency;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Currency;
import java.util.Date;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class CurrencyConversion implements Serializable {
private static final long serialVersionUID = 1L;
// USD
private Currency sourceCurrency;
// BRL
private Currency targetCurrency;
private Date rateTimestamp;
private BigDecimal amount;
// 1 USD = 2.21995 BRL
private BigDecimal amountSourceToTarget;
// 1 BRL = 0.450461 USD
private BigDecimal amountTargetToSource;
public Currency getSourceCurrency() {
return sourceCurrency;
}
public Currency getTargetCurrency() {
return targetCurrency;
}
public Date getRateTimestamp() {
return rateTimestamp;
}
public BigDecimal getAmount() {
return amount;
}
public BigDecimal getAmountSourceToTarget() {
return amountSourceToTarget;
}
public BigDecimal getAmountTargetToSource() {
return amountTargetToSource;
}
public static CurrencyConversion parse(String html) throws ParseException {
DateFormat df = new SimpleDateFormat(
"'Live rates at 'yyyy.MM.dd HH:mm:ss ZZZ");
Pattern valores = Pattern
.compile("<span class=\"XEsmall\">(.*)</span>");
Matcher matcher = valores.matcher(html);
Date data = (matcher.find()) ? df.parse(matcher.group(1)) : null;
String sourceToTarget = (matcher.find()) ? matcher.group(1) : null;
String targetToSource = (matcher.find()) ? matcher.group(1) : null;
sourceToTarget = cleanCopyright(sourceToTarget);
targetToSource = cleanCopyright(targetToSource);
CurrencyConversion cc = new CurrencyConversion();
Pattern p = Pattern
.compile("([0-9\\.]+) (\\w{3}) = ([0-9\\.]+) (\\w{3})");
Matcher matcher2 = p.matcher(sourceToTarget);
if (matcher2.find()) {
cc.amount = new BigDecimal(matcher2.group(1));
cc.sourceCurrency = Currency.getInstance(matcher2.group(2));
cc.amountSourceToTarget = new BigDecimal(matcher2.group(3));
cc.targetCurrency = Currency.getInstance(matcher2.group(4));
Matcher matcher3 = p.matcher(targetToSource);
if (matcher3.find()) {
cc.amountTargetToSource = new BigDecimal(matcher3.group(3));
}
}
cc.rateTimestamp = data;
return cc;
}
private static String cleanCopyright(String copyrightedString) {
String ret = copyrightedString;
if (ret != null) {
int idx = ret.indexOf("<!--");
ret = ret.substring(0, idx);
}
return ret;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("[");
for (Field field : getClass().getDeclaredFields()) {
try {
sb.append(field.getName()).append("=").append(field.get(this))
.append(", ");
} catch (Exception e) {
e.printStackTrace();
}
}
sb.deleteCharAt(sb.length() - 1);
sb.deleteCharAt(sb.length() - 1);
sb.append("]");
return sb.toString();
}
}
É necessário escrever um MessageBodyReader para que o provider JAX-RS possa entender como traduzir o conteúdo HTML no objeto CurrencyConversion.
package com.george.rest.currency;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.text.ParseException;
import javax.ws.rs.Consumes;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.MessageBodyReader;
import javax.ws.rs.ext.Provider;
@Provider
@Consumes("text/html")
public class CurrencyConversionMessageBodyReader implements
MessageBodyReader<CurrencyConversion> {
public boolean isReadable(Class<?> type, Type genericType,
Annotation[] annotations, MediaType mediaType) {
return type == CurrencyConversion.class || type == String.class;
}
public CurrencyConversion readFrom(Class<CurrencyConversion> type,
Type genericType, Annotation[] annotations, MediaType mediaType,
MultivaluedMap<String, String> httpHeaders, InputStream entityStream)
throws IOException, WebApplicationException {
String input = readInputStreamToString(entityStream);
try {
return CurrencyConversion.parse(input);
} catch (ParseException e) {
throw new WebApplicationException(e);
}
}
private String readInputStreamToString(InputStream entityStream)
throws IOException {
byte[] b = new byte[1024];
ByteArrayOutputStream baos = new ByteArrayOutputStream();
for (int i = entityStream.read(b); i != -1; i = entityStream.read(b)) {
baos.write(b, 0, i);
}
return baos.toString();
}
}
Por último, mas não menos importante, está a chamada do serviço REST usando o JBoss RestEasy Client Framework.Neste exemplo, pretendo saber o valor de 1 USD (US Dollar) em BRL (Brazilian Real).
package com.george.app;
import java.math.BigDecimal;
import java.util.Currency;
import java.util.Locale;
import org.jboss.resteasy.client.ProxyFactory;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
import com.george.rest.currency.CurrencyConversion;
import com.george.rest.currency.CurrencyConversionMessageBodyReader;
import com.george.rest.currency.ICurrencyConverter;
public class CurrencyTest {
public static void main(String[] args) throws Exception {
Currency brl = Currency.getInstance(new Locale("pt","BR"));
Currency usd = Currency.getInstance(Locale.US);
//Registra o Reader explicitamente
ResteasyProviderFactory provider = ResteasyProviderFactory.getInstance();
provider.addMessageBodyReader(CurrencyConversionMessageBodyReader.class);
// Chama o serviço de conversão
ICurrencyConverter currencyConverter = ProxyFactory.create(ICurrencyConverter.class, ICurrencyConverter.REST_SERVICE_URL);
CurrencyConversion response =
currencyConverter.convert(new BigDecimal("1.00"),brl,usd);
System.out.printf("Rate Timestamp: %1$tF %tT %n", response.getRateTimestamp());
System.out.printf("Amount: %.2f", response.getAmountSourceToTarget());
}
}
Você terá uma saída semelhante a seguinte:
Rate Timestamp: 2010-04-27 01:49:28
Amount: 0,57
Grande george, como vai? como sempre jboss… o carro chefe da consultoria milionária gegastaldi hehehe! Forte abraço!
Dae George,
O Ademir me passou teu BLOG, achei muito bem escrito muito bom mesmo parabens! :)
Sucesso.
Tive alguns erros ao tentar esse procedimento. Isso foi postado em 2010, hoje ainda funciona essa aplicação ou eu que estou fazendo errado mesmo?
Olá Augusto,
Que erros você está tendo ? Talvez a versão do RestEasy tenha mudado. Vou criar um repositório no Github com este código e aí você pode baixar.