Gastaldi's Blog

Mais um blog sobre Java …

JBoss Forge 1.0.6.Final Liberado !

É com grande prazer que informo a liberação do JBoss Forge 1.0.6.Final ! Essa é a primeira liberação depois que me tornei core developer na Red Hat !

O JBoss Forge é uma IDE baseada em linha de comando, que auxilia na criação de aplicações Java EE 6.0, pronta para produção. Possui plugins que geram interfaces do usuário, configuram a aplicação baseado no servidor de aplicação desejado e muito mais !

Foram corrigidos + de 15 bugs desde a versão 1.0.5, e adicionado várias melhorias, entre elas:

  • Licença EPL: A partir desta versão, o JBoss Forge passa a utilizar a licença EPL (Eclipse Public License). Isto para resolver o problema de que, com  a licença anterior (LGPL), seria legalmente restrita a facilildade de criação de plugins por terceiros, portanto com isso removemos esta restrição.
  • Layout Redesenhado: O Layout das aplicações geradas pelo JBoss Forge foram melhorados. Agora as páginas geradas seguem o padrão HTML 5 e utilizam o framework Twitter Bootstrap. Tudo para facilitar a manutenção e diminuir a quantidade de código necessário
  • “Você quis dizer ?”: Agora ao tentar executar um comando inválido, o JBoss Forge irá apresentar sugestões similares para você.
  • Plugins, Plugins, Plugins ! : O JBoss Forge vem recheado de plugins, que facilitam a sua vida ainda mais ! Nesta versão existem plugins para cópia de arquivos (“cp”), criação de arquivos (“touch”) além de plugins criados por nossos colaboradores que você pode instalar a vontade, tais como um plugin para desfazer alterações (undo), um plugin para fornecer dicas e orientação durante o desenvolvimento do projeto (forge-hint-plugin – em desenvolvimento),um plugin para criação de artefatos para o JBoss SwitchYard,, entre outros !

Gostaria de agradecer a todos os envolvidos nesta liberação, em especial ao José Donizetti e ao Jevgeni Zelenkov, que auxiliaram na criação de novos plugins e, além de corrigir bugs, auxiliaram na transição para a licença EPL. Agradeço também ao Lincoln Baxter (Líder do projeto) pelas reuniões e análise de códigos.

Confira o Release notes e a página oficial do projeto.

May the Forge be with you ! :D

Liberado Seam Reports 3.0.0.Alpha1 !

Mais um módulo Seam 3 (Reports) foi liberado ! A versão 3.0.0.Alpha1 é a primeira versão do Seam Reports já lançada.

Seam Reports é uma extensão CDI que fornece uma API simples para compilação, população e renderização de relatórios utilizando frameworks de relatório já disponíveis no mercado.

Devo postar mais informações sobre este módulo em breve.

Mais informações em: http://www.seamframework.org/Seam3/ReportsModule

JCP Member

Finalmente recebi a confirmação de registro no JCP (http://jcp.org). Com isso, posso criar JSRs, juntar-me a expert groups e enfim, envolver-me mais com a comunidade Java.

Pode conferir em http://jcp.org/en/participation/members/G.

Multi-Tenância no Hibernate

Se você já usou o Hibernate, sabe que ele é um framework poderoso para mapeamento objeto-relacional. Neste post apresento como podemos resolver de uma forma simples uma necessidade bastante comum: Acessar diversos bancos de dados com apenas um hibernate.cfg mapeado.

A solução para este problema depende na forma de como os dados são recuperados do seu database:

1) Se os bancos estiverem separados por conexões diferentes, crie uma classe que implemente a interface ConnectionProvider e configure no Hibernate.

2) Se o banco for gerenciado por apenas um pool de conexões, mas acessa dados de outros bancos a partir da mudança do SCHEMA, você pode utilizar um Interceptor para isso:

import static org.apache.commons.lang.StringUtils.defaultIfEmpty;

import java.util.regex.Pattern;
import org.hibernate.EmptyInterceptor;

/**
 * Interceptor para alterar SCHEMA dinamicamente
 * 
 * @author george
 * 
 */
public class SchemaReplaceInterceptor extends EmptyInterceptor {

	private static final long serialVersionUID = 1L;
	private Pattern pattern;

	private String defaultSchema;

	public SchemaReplaceInterceptor(String token, String defaultSchema) {
		pattern = Pattern.compile(token);
		this.defaultSchema = defaultSchema;
	}

	@Override
	public String onPrepareStatement(String sql) {
		String novoSchema = recuperaNomeSchema();
		String novoSQL = pattern.matcher(sql).replaceAll(novoSchema);
		return super.onPrepareStatement(novoSQL);
	}

	protected String recuperaNomeSchema() {
             //TODO: Implementar - Recuperar de um ThreadLocal 
	}
}

Filtro para sessão expirada

Se você programa em Java EE para aplicações Web, já deve ter conhecimento do objeto HttpSession e a sua expiração por inatividade. Neste post apresento um exemplo de um filtro que permite detectar no momento da requisição se a sessão expirou e assim evitar erros como Session already invalidated e afins:

package com.george.utils;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

/**
 * Filtro para controle de expiração da sessão.
 * 
 * Caso a sessão seja expirada, redireciona para uma URL definida
 * 
 * @author George Gastaldi
 * 
 */
public class SessionExpiredFilter implements Filter {

	/**
	 * @see Filter#init(FilterConfig)
	 */
	public void init(FilterConfig filterConfig) throws ServletException {
	}

	/**
	 * Verifica se a sessão não expirou, caso contrário joga erro 408 (REQUEST TIMEOUT).
	 */
	public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain chain) throws IOException, ServletException {
		HttpServletRequest httpRequest = (HttpServletRequest) request;
		HttpServletResponse httpResponse = (HttpServletResponse) response;

		// Verifica se existe uma sessão para comparar e se a session ID
		// retornada pelo cliente é a mesma
		boolean sessionInvalidated = isSessionInvalidated(httpRequest);
		if (sessionInvalidated) {
			httpResponse.addCookie(createCookie(httpRequest));
			httpResponse.sendError(HttpServletResponse.SC_REQUEST_TIMEOUT);
		} else {
			chain.doFilter(request, response);
		}
	}
	
	/**
	 * Retorna se a sessão foi invalidada
	 * @param httpRequest
	 * @return
	 */
	boolean isSessionInvalidated(HttpServletRequest httpRequest) {
		String requestedSessionId = httpRequest.getRequestedSessionId();
		HttpSession session = httpRequest.getSession(false);	
		if (session == null) {
			return (requestedSessionId != null);
		} else {
			return (requestedSessionId != null && !session.getId().equals(requestedSessionId));
		}
	}
	
	/**
	 * Mata o cookie de sessão do browser
	 * @param request
	 * @return
	 */
	private Cookie createCookie(HttpServletRequest request) {
		Cookie cookie = new Cookie("JSESSIONID", request.getRequestedSessionId());
		cookie.setMaxAge(0); // Matar o cookie
		String contextPath = request.getContextPath();
		if (!"".equals(contextPath)) {
			cookie.setPath(contextPath);
		} else {
			cookie.setPath("/");
		}
		return cookie;
	}


	/**
	 * @see Filter#destroy() 
	 */
	public void destroy() {
	}

}

Você pode configurar no seu web.xml o filtro da seguinte maneira:

<filter>
	<filter-name>Session Expired Filter</filter-name>
	<description>Filtro para sessões expiradas. Lança erro 408 se a sessão expirou</description>
	<filter-class>com.george.utils.SessionExpiredFilter</filter-class>
</filter>
<filter-mapping>
	<filter-name>Session Expired Filter</filter-name>
	<url-pattern>/*</url-pattern>
</filter-mapping>

Criando uma solução para atualização de aplicações remotas

Imagine o seguinte: Você desenvolve uma aplicação (web ou não) e ela roda em um ambiente remoto (o ambiente de produção do seu cliente, seja em um servidor de aplicação JBoss ou Tomcat instalado no ambiente remoto) e você precisa garantir que a cada nova release da sua aplicação, o cliente tenha sempre a última versão. Neste post pretendo mostrar como você pode fazê-lo utilizando o Apache Maven e o Apache Ant para gerenciamento das versões do seu projeto e automatização de tarefas.

Não pretendo demonstrar uma solução completa, mas apresentar uma prova de conceito que pode ser melhorado. Para quem não conhece o Maven ou o Ant, sugiro ler as respectivas documentações. Para que funcione, você já deve ter um repositório remoto Maven configurado (sugiro o Sonatype Nexus) e (obviamente) a sua aplicação já deve estar “mavenizada”.

Você pode instalar o Apache Ant na sua maquina que vai rodar o script e adicionar o JAR do Maven Tasks for Ant no lib ou então seguir os passos abaixo para criar uma distribuição do Ant já com as bibliotecas necessárias. Se você não gosta de Maven, então sugiro passar pro passo 3 (onde está o início do script)

1) Queremos que esta solução seja instalável pelo cliente, logo deve ser ao menos distribuída em um arquivo ZIP que pode ser descompactado. Começamos a criar um projeto Maven e logo teremos o seguinte pom.xml:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.george.app</groupId>
	<artifactId>george-atualizador</artifactId>
	<version>1.0.0-SNAPSHOT</version>
	<name>George :: Atualizador</name>
	<packaging>pom</packaging>
	<dependencies>
		<dependency>
			<groupId>org.apache.ant</groupId>
			<artifactId>ant</artifactId>
			<version>1.8.1</version>
		</dependency>
		<dependency>
			<groupId>org.apache.maven</groupId>
			<artifactId>maven-ant-tasks</artifactId>
			<version>2.1.1</version>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-assembly-plugin</artifactId>
				<version>2.2</version>
				<executions>
					<execution>
						<id>gera-zip</id>
						<phase>package</phase>
						<goals>
							<goal>single</goal>
						</goals>
						<inherited>false</inherited>
						<configuration>
							<descriptors>
								<descriptor>src/main/assembly/instalador.xml</descriptor>
							</descriptors>
						</configuration>
					</execution>
				</executions>
			</plugin>
		</plugins>
	</build>
</project>

Este pom.xml é a base do nosso projeto. Ele tem como dependência o Apache Ant e uma extensão que permite invocar tarefas do Maven dentro de um script Ant.

2) Note que é usado também um plugin (assembly) para montagem do ZIP. Ele tem a seguinte estrutura:

<assembly
	xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
	<id>instalador</id>
	<formats>
		<format>zip</format>
	</formats>
	<fileSets>
		<fileSet>
			<directory>src/main/assembly/bin</directory>
			<outputDirectory>bin</outputDirectory>
		</fileSet>
		<fileSet>
			<directory>src/main/assembly/conf</directory>
			<outputDirectory>conf</outputDirectory>
		</fileSet>
		<fileSet>
			<directory>src/main/assembly</directory>
			<includes>
				<include>build.xml</include>
			</includes>
			<outputDirectory>/</outputDirectory>
		</fileSet>
	</fileSets>
	<dependencySets>
		<dependencySet>
			<outputDirectory>lib</outputDirectory>
			<useProjectArtifact>false</useProjectArtifact>
			<outputFileNameMapping>${artifact.artifactId}.${artifact.extension}
			</outputFileNameMapping>
		</dependencySet>
	</dependencySets>
</assembly>

3) Agora você só precisa do arquivo build.xml (o mais importante – que vai conter as ações de atualização da aplicação) e os executáveis (Arquivos BAT) do Apache Ant (para invocá-lo quando necessário).


<?xml version="1.0" encoding="UTF-8"?>
<project name="project" default="default" xmlns:artifact="antlib:org.apache.maven.artifact.ant">
	<!-- ================================= 
          target: default              
         ================================= -->
	<target name="default" depends="resolver-dependencias" description="description">
		<copy todir="temp">
			<fileset refid="dependency.fileset" />
			<mapper classname="org.apache.maven.artifact.ant.VersionMapper" from="${dependency.versions}" to="flatten" />
		</copy>
	</target>

	<target name="resolver-dependencias">
		<artifact:dependencies filesetId="dependency.fileset" versionsId="dependency.versions" settingsFile="conf/settings.xml">
			<dependency groupId="com.george.app" artifactId="minha-app-maven" version="[1.0.0,2.0.0)" />
		</artifact:dependencies>
	</target>
</project>

Este script vai conectar no seu repositório remoto (está especificado no seu arquivo conf/settings.xml – veja como criar um aqui), baixar a última versão disponível que esteja entre a versão 1.0.0 e menor que a 2.0.0 (mais informações sobre o versionamento maven aqui) e gravar em uma pasta temp sem a informação de versão (que é o padrão do Maven).

A partir daí, você pode incrementar o seu script para copiar o WAR para o seu Tomcat ou JBoss e até mesmo agendá-lo (usando cron ou o agendador do windows mesmo, ou porque não o Hudson) para executá-lo toda noite, enfim, as possibilidades são muitas.

Enfim, este post teve a intenção de mostrar que o Apache Maven não é só mais uma ferramenta de builld, mas que usado em conjunto com o Apache Ant, chega a agradar gregos e troianos ! :)

Serviço REST para postar mensagens no Pastebin.com

O PasteBin é um serviço que permite que você publique trechos de código e conteúdo temporário em geral. Normalmente para evitar ter que salvar num arquivo texto e transmitir via email, ou outro protocolo, basta apenas você entrar no site, colar o conteúdo que você deseja compartilhar e aí disponibilizar a URL que ele irá gerar para quem você quiser.

É possível integrar com o JBoss RestEasy para que essa publicação seja encapsulada no Client Framework, usando a API definida em http://pastebin.com/api.php

Crie a interface de serviço

import javax.ws.rs.Consumes;
import javax.ws.rs.FormParam;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import org.jboss.resteasy.annotations.Form;

/**
 * Publica um conteúdo no Pastebin.com
 *
 * @author George Gastaldi
 * 
 */
@Path("/")
public interface PasteBinService {

	/**
	 * Publica o código fonte e retorna a URL publicada
	 * 
	 * @param pasteCode
	 * @return
	 */
	@POST
	@Path("/api_public.php")
	@Consumes(MediaType.TEXT_PLAIN)
	@Produces(MediaType.TEXT_PLAIN)
	public String publish(@FormParam("paste_code") String pasteCode);
	/**
	 * Publica o código fonte e retorna a URL publicada
	 * 
	 * @param pasteCode
	 * @return
	 */
	@POST
	@Path("/api_public.php")
	@Consumes(MediaType.TEXT_PLAIN)
	@Produces(MediaType.TEXT_PLAIN)
	public String publish(@FormParam("paste_code") String pasteCode, @Form PasteBinParams params);
}

Aqui estou usando um recurso bacana do RestEasy que é o encapsulamento dos parametros em uma classe Java a parte.
Aqui está o código-fonte da classe de parametros:


import javax.ws.rs.FormParam;

public class PasteBinParams {

	/**
	 * - for adding a title or name to your paste.
	 */
	@FormParam("paste_name")
	private String pasteName;

	/**
	 * - for sending confirmation email with paste link.
	 */
	@FormParam("paste_email")
	private String pasteEmail;

	/**
	 * - for using a certain subdomain.
	 */
	@FormParam("paste_subdomain")
	private String pasteSubdomain;

	/**
	 * - for making it public (0) or private (1).
	 */
	@FormParam("paste_private")
	private int pastePrivate;

	/**
	 * - for adding expiration date. N = Never, 10M = 10 Minutes, 1H = 1 Hour,
	 * 1D = 1 Day, 1M = 1 Month.
	 */
	@FormParam("paste_expire_date")
	private String pasteExpireDate;

	/**
	 * - for adding syntax highlighting.
	 */
	@FormParam("paste_format")
	private String pasteFormat;
     
         ... getters e setters omitidos ...
}

Agora é só testar !

	public static void main(String[] args) {
		// Necessário inicializar
		RegisterBuiltin.register(ResteasyProviderFactory.getInstance());
		PasteBinService service = ProxyFactory.create(PasteBinService.class,"http://pastebin.com");
		String url = service.publish("Hello REST World !");

		System.out.println("URL: " + url);

		PasteBinParams params = new PasteBinParams();
		params.setPasteName("George Gastaldi");
		params.setPasteEmail("gastaldi@apache.org");
		params.setPasteFormat("xml");
		params.setPasteSubdomain("gastaldi");
		params.setPasteExpireDate("10M");

		url = service.publish("<root><tag>Hello XML World !</tag></root>",
				params);
		System.out.println("URL: " + url);
	}

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

Registrar uma String no JNDI

Em muitas aplicações, existem parâmetros que variam de acordo com o ambiente (pools de conexão, roles, etc) e que são publicadas na árvore JNDI da aplicação. Para parametrizarmos uma string qualquer (seja ela uma URL, uma informação qualquer) muitos desenvolvedores preferem criar um arquivo de propriedades ou deixar fixo no código (bleargh !). Minha sugestão é utilizar o contexto JNDI. Para isso faça o seguinte:

Registre o serviço abaixo em um arquivo com sufixo -service.xml (pode ser no jboss-service.xml dentro do conf):

<mbean code="org.jboss.naming.JNDIBindingServiceMgr"
         name="jboss.tests:service=JNDIBindingServiceMgr">
      <attribute name="BindingsConfig" serialDataType="jbxb">
         <jndi:bindings
            xmlns:xs="http://www.w3.org/2001/XMLSchema-instance"
            xmlns:jndi="urn:jboss:jndi-binding-service:1.0"
            xs:schemaLocation="urn:jboss:jndi-binding-service:1.0 resource:jndi-binding-service_1_0.xsd">
            <jndi:binding name="urls/MinhaURL">
               <jndi:value type="java.lang.String">http://www.meuserver.com/service</jndi:value>
            </jndi:binding>
         </jndi:bindings>
      </attribute>
      <depends>jboss:service=Naming</depends>
   </mbean>

Na nossa aplicação web, devemos alterar o web.xml para incluir o seguinte resource-ref:

<resource-ref>
        <res-ref-name>QualquerCoisa</res-ref-name>
        <res-type>java.lang.String</res-type>
        <res-auth>Container</res-auth>
        <res-sharing-scope>Shareable</res-sharing-scope>
</resource-ref>	

e no jboss-web.xml :

<jboss-web>
	<resource-ref>
		<res-ref-name>QualquerCoisa</res-ref-name>
		<res-type>java.lang.String</res-type>
		<jndi-name>urls/MinhaURL</jndi-name>
	</resource-ref>
</jboss-web>

Pronto ! Agora é só fazer:

InitialContext ctx = new InitialContext();
String strNoJNDI = (String) ctx.lookup("java:comp/env/QualquerCoisa");

e você terá o valor publicado no seu servidor de aplicação (totalmente desacoplado da aplicação).

Registrando um MBean no JNDI

No JBoss, para registrar um serviço MBean no JNDI da sua aplicação (a fim de ser acessível remotamente), utilize o seguinte serviço:

	<mbean code="org.jboss.invocation.jrmp.server.JRMPProxyFactory"
		name="george.proxy:type=adaptor,name=MeuProxy,protocol=jrmp,service=proxyFactory">
		<!-- Use the standard JRMPInvoker from conf/jboss-service.xxml -->
		<depends optional-attribute-name="InvokerName">jboss:service=invoker,type=jrmp</depends>
		<!-- The target MBean -->
		<depends optional-attribute-name="TargetName">dominio:name=MeuMBean</depends>
		<!-- Where to bind the proxy factory -->
		<attribute name="JndiName">NomeJNDI</attribute>
		<!-- Invoke target method instead of invoke(Invocation mi) -->
		<attribute name="InvokeTargetMethod">true</attribute>
		<!-- Comma-separated list of exported interfaces -->
		<attribute name="ExportedInterfaces">InterfaceDoMBean</attribute>
		<!-- client-side interceptors -->
		<attribute name="ClientInterceptors">
			<interceptors>
				<interceptor>org.jboss.proxy.ClientMethodInterceptor</interceptor>
				<interceptor>org.jboss.proxy.SecurityInterceptor</interceptor>
				<interceptor>org.jboss.jmx.connector.invoker.client.InvokerAdaptorClientInterceptor</interceptor>
				<interceptor>org.jboss.invocation.InvokerInterceptor</interceptor>
			</interceptors>
		</attribute>
	</mbean>
Seguir

Obtenha todo post novo entregue na sua caixa de entrada.