Obtendo uma referência a projetos Maven na construção de plugins!

julho 13, 2009

Ultimamente estou desenvolvendo um plugin para Maven 2 que gera código fonte via templates Freemarker, e para isto existe a necessidade de ser ter todas as informações sobre o projeto  que irá usar o plugin e também acesso ao seu classloader.

Para isto, basta apenas solicitar uma referência ao Maven dentro de uma classe  especializada de AbstractMojo da seguinte forma:

/**
* @parameter expression="${project}"
* @required
* @readonly
*/<span>
private MavenProject mavenProject;

Outra coisa importante, para conseguir enxergar esta classe no desenvolvimento de plugins, além da API basica provida pelo maven, a seguinte referência tem que ser adicionada:

<dependency>
      <groupid>org.apache.maven</groupid>
      <artifactid>maven-project</artifactid>
      <version>${mvnplugin.version}</version>
</dependency>

Solução simples, mas muito difícil de achar uma referência na internet sobre isso.

Para quem se interessar, e quiser saber além, no site da Sonatype (aquela do Nexus) tem uma referência muito boa sobre Maven, chamada Maven: The definitive guide.

Snnipets em Groovy!

julho 6, 2009

Estava navegando hoje pela internet buscando mais materiais sobre Groovy/Grails quando encontro este site chamado Groovy Almanac. Um conjunto de snippets dos mais variados assuntos, que indiscutivelmente agilizam o aprendizado e a adoção da linguagem. Simples, e bem elaborado.

Uma forma excelente para uma base de conhecimento!

Automatizando a injeção de mecanismos de Log com o Spring!

julho 4, 2009

O Spring possui um mecanismo de hooks que nos permitem manipular os beans sobre seu contexto quando estes são instanciados, também conhecido como BeanPostProcessor.

Esta interface define dois métodos:

  • postProcessAfterInitialization – Intercepta um bean dentro do contexto da aplicação que já foi instanciado e suas propriedades já foram carregadas, porém, depois de chamadas a mêtodos customizados de inicialização, por exemplo.
  • postProcessBeforeInitialization -  Intercepta um bean dentro do contexto da aplicação que já foi instanciado e suas propriedades também já foram carregadas, porém, antes de chamadas a mêtodos customizados de inicialização, por exemplo.

Com este gancho podemos criar um utilitário que injete a dependência de uma classe do tipo org.apache.log4j.Logger nas classes que necessitem fazer algum uso do mecanismo de log, através de uma anotação ou por convenção mesmo.

Definição da anotação de Log:

/**
 * Anotação marcadora de log.
 * 
 * @author Erico Marineli
 * 
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Log {}

Apeser de ter uma anotação, vou utilizar também um mecanismo de convencão, o seja, caso o desenvolvedor chamar o atributo do tipo Logger de “log” ou “logger”, não será necessário o uso da anotação, o logger será adicionado automaticamente.

Definição classe que fará todo o trabalho para injeção:

/**
 * PostProcessor para acrescentar no bean um Logger provido pelo LOG4J. O logger
 * pode ser inserido via a anotação Log ou por convenção (logger ou log)
 * 
 * @author Erico Marineli
 * 
 */
public class Log4jBeanPostProcessor implements BeanPostProcessor {

        /**
         * Realiza cache das instâncias de log criadas.
         */
	private static Map<class <?>, Logger> cacheLoggers = new HashMap</class><class <?>, Logger>();
	
        /**
         * Nome de pacote para filtro. Util para restringir a busca pela anotação e convencões
         * apenas em classes do projeto.
         */
	private String packageFilter;

	/**
	 * @see BeanPostProcessor#postProcessAfterInitialization(Object, String)
	 */
	public Object postProcessAfterInitialization(Object bean, String beanName)
			throws BeansException {
		return bean;
	}

	/**
	 * @see BeanPostProcessor#postProcessBeforeInitialization(Object, String)
	 */
	@SuppressWarnings("unchecked")
	public Object postProcessBeforeInitialization(final Object bean,
			String beanName) throws BeansException {
		Class clazz = bean.getClass();
		for (Field field : clazz.getDeclaredFields()) {

			/*
			 * Verifica se a classe possui a anotação requerida ou se segue
			 * algum padrão de nomenclatura. Deve ser também do tipo
			 * org.apache.log4j.Logger e estar no padrão do package (caso informado).
			 */
			if ((field.getAnnotation(Log.class) != null
					|| field.getName().equalsIgnoreCase("logger")
					|| field.getName().equalsIgnoreCase("log")) 
					&& field.getType().equals(Logger.class) 
					&& (getPackageFilter() == null || clazz.getPackage()
							.getName().startsWith(getPackageFilter()))) {

				field.setAccessible(true);

				/* utiliza cache de log */
				Logger logger = null;
				if (cacheLoggers.containsKey(clazz)) {
					logger = (Logger) cacheLoggers.get(clazz);
				} else {
					logger = Logger.getLogger(clazz);
					cacheLoggers.put(clazz, logger);
				}
				
				/* acrescenta o log ao campo */
				try {
					field.set(bean, logger);
				} catch (Exception e) {
					e.printStackTrace();
					throw new RuntimeException(e);
				}
			}
		}
		return bean;
	}

	/**
	 * @return the packageFilter
	 */
	public String getPackageFilter() {
		return packageFilter;
	}

	/**
	 * @param packageFilter the packageFilter to set
	 */
	public void setPackageFilter(String packageFilter) {
		this.packageFilter = packageFilter;
	}
	
}

Finalmente, definição do bean no arquivo de configuração do contexto da aplicaçao:

	<!-- Log4j BeanPostProcessors -->
	<bean class="br.com.app.spring.logger.Log4jBeanPostProcessor" >
	   <property name="packageFilter" value="br.com.app" />
	</bean>

Note que podemos ou não passar um filtro de pacotes. Este filtro é interessante para que somente seja verificado classes dos nossos projetos, pois, o hook de BeanPostProcessor é acionado para cada classe que estiver sendo referenciada no contexto da aplicação do Spring.

Veja na prática como fica:

/**
 * Classe de testes de injeção automática de logger.
 * 
 * @author Erico Marineli
 *
 */
@ContextConfiguration(locations = { "/applicationContextTest.xml" })
public class ResourcesTest extends AbstractJUnit4SpringContextTests {

	/**
	 * Obtém o log através da anotação.
	 */
	@Log
	private Logger logger;
	
	/**
	 * Obtém o log através da CONVENÇÃO.
	 * Aqui, o atributo poderia se chamar logger também.
	 */
	private static Logger log;

	@Test
	public void testLogResources() {
		Assert.assertNotNull(logger);
		Assert.assertNotNull(log);
		
		logger.debug("Logger ok");
		log.debug("Log ok");
	}
}

Tudo que fizemos é muito simples pois é feito dentro dos mecanismos do próprio Spring fornece. Diversas outras coisas podem ser feitas neste sentido,  pois quanto mais transparente os mecanismo desta natureza para os desenvolvedores, melhor.

Coding Rules Engines – Qual escolher ?

julho 1, 2009

Quanto o assunto é qualidade de código em Java, para quem fica na dúvida se deve usar o Checkstyle, o FindBugs ou PMD ou até um combinado deles, segue um ótimo post no site do Sonar sobre o papel de cada ferramenta.

Basicamente a idéia é que estas ferramentas se complementam, e não devemos pensar que uma seja melhor que a outra. O post mostra onde cada uma se enquadra e também apresenta uma outra nova, que verifica se o desenvolvedor está seguindo a arquitetura corretamente (acho essa espetacular!!!).

Eu sempre fui usuário da dupla Checkstyle + PMD, e o FindBugs nunca me fez falta, somente os dois juntos já garantiam uma qualidade satisfatória do código fonte produzido e eu não necessitava verificar código já compilado. Vejo relatos também de desenvolvedores que utilizavam o Checkstyle + FindBugs e gostavam muito.

Mas no final das contas, acho melhor pecar pelo excesso do que pela falta não é ?

Repositório Maven com Nexus em 5 minutos!

julho 1, 2009

Veja como é extremamente simples criar um repositório proxy para utilização do Apache Maven2 com a ferramenta Sonatype Nexus (Open Source).

Após o download, basta instalar a aplicação, empacotada em formato de um web archive (war) em um servidor de aplicações ou até mesmo em um servlet container. Com a aplicação em funcionamento, ao clicarmos no link Repositories, veremos a seguinte imagem:

Nexus Repositorios

Nexus Repositórios

Aqui iremos trabalhar com apenas três tipos de repositórios:

  • Hosted: Repositório local, os artefatos são obtidos e inseridos no servidor que hospeda o aplicativo;
  • Proxy: Repositório que age como um proxy para um repositório que está remoto ao aplicativo;
  • Group: Agrega e ordena um conjunto de repositórios, incluindo os dois citados acima.

Este três tipos de repositórios são essenciais para montarmos um repositório enterprise. O hosted será responsável por agregar os artefatos por nós produzidos e que ficarão sobre nossa guarda, já o proxy irá apontar para os repositórios Maven distribuídos pela internet, e qualquer outro repositório remoto ao nosso (sim, podemos ter outros repositórios sobre nossa guarda e criarmos repositórios proxy sobre eles). O repositório do tipo Group é quem vai agregar nossos repositórios locais e remotos, em uma ordem de busca específica.

Neste exemplo criei dois repositórios do tipo hosted, um para hospedar os artefatos de release de projeto e outro para hospenar os artefatos que ainda não em desenvolvimento ou que ainda não podem ser disponibilizados como releases, ou seja, snapshots. Os outros repositórios tipos já vêm com a configuração básica do Nexus

Para conseguirmos utilizar o repositório com a linha de comando do maven, precisamos ajustar/criar 2 arquivos:

  • settings.xml: Arquivo de mais alto nível na hierarquia, irá guardar as configurações de acesso ao servidor Nexus e os dados para autenticação neste;
  • pom.xml: Responsável por guardar as informações sobre gerenciamento de distribuição dos artefatos gerados nos projetos e também indicar o endereço do repositório Nexus, para que este seja o repositório proxy de qualquer projeto.

Quanto ao arquivo referente a organização de projetos (pom.xml) a idéia é criá-lo para ser comum a qua lquer projeto, ou seja, ser o parent da hierarquia.

Segue a configuração dos arquivos:

[settings.xml]

<settings>
 
  <servers>
     <server>
       <id>nexus</id>
       <username>emarineli</username>
       <password>emarineli</password>
     </server>
  </servers>

</settings>

Os dados de user e password são os mesmo utilizados para se realizar o login na ferramenta.

[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/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>br.com.emarineli.estudo</groupId>
    <artifactId>common-pom</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>
    <name>common pom</name>
    <url>http://emarineli.wordpress.com</url>
 
        <prerequisites>
                <maven>2.0.9</maven>
        </prerequisites>
         
    <description>
        Pom raiz para desenvolvimento de sistemas utilizando Maven 2.
        Contém todas as informações necessárias sobre SCM e Repositórios.
    </description>
 
    <developers>
        <developer>
            <name>Erico Marineli</name>
            <id>emarineli</id>
            <email>emarineli@gmail.com</email>
            <organization>SCAT</organization>
            <roles>
                <role>Programmer</role>
            </roles>
            <timezone>-3</timezone>
        </developer>
    </developers>
 
    <distributionManagement>
 
        <repository>
            <id>nexus</id>
            <name>Repositorio de Releases</name>
            <url>http://localhost:8080/nexus-webapp-1.3.4/content/repositories/emarineli-repo-release/</url>
        </repository>
 
        <snapshotRepository>
            <id>nexus</id>
            <name>Repositorios de Snapshots</name>
            <url>http://localhost:8080/nexus-webapp-1.3.4/content/repositories/emarineli-repo-snapshot/</url>
        </snapshotRepository>
 
    </distributionManagement>
 
    <repositories>
        <repository>
            <id>central</id>
            <url>http://localhost:8080/nexus-webapp-1.3.4/content/groups/public</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
        <repository>
            <id>snapshots</id>
            <url>http://localhost:8080/nexus-webapp-1.3.4/content/groups/public-snapshots</url>
            <releases>
                <enabled>false</enabled>
            </releases>
        </repository>
    </repositories>
    <pluginRepositories>
              <pluginRepository>
                    <id>central</id>
                    <url>http://localhost:8080/nexus-webapp-1.3.4/content/groups/public</url>
                    <snapshots>
                          <enabled>false</enabled>
                    </snapshots>
              </pluginRepository>
              <pluginRepository>
                    <id>snapshots</id>
                    <url>http://localhost:8080/nexus-webapp-1.3.4/content/groups/public-snapshots</url>
                    <releases>
                          <enabled>false</enabled>
                    </releases>
              </pluginRepository>
        </pluginRepositories>
  
</project>

Repare que o mesmo identificador utilizado no arquivo settings.xml deve ser passado como identificador no repositório de distribuição de nossos projetos no arquivo pom.xml, além de substituírmos também neste arquivo os repositórios central e snapshots.

Ao final, após utilizar o maven você já poderá ver os artefatos que foram trazidos dos respositórios proxy hospedados no Nexus, como mostra a figura abaixo:

Artefatos hospedados no Nexus

Artefatos hospedados no Nexus

Simples, não? Agora, qualquer pom que herde as configurações deste criado acima, passará a utilizar o repositório enterprise.

Existem diversos tipos de configurações que podemos fazer, como utilizar profiles, adição de configurações no settings ao invés do pom criado acima, cluster redundante com a ferramenta para garantir auta disponibilidade e balanceamento de carga, e  diversas outras. Porém a idéia foi fazer uma configuração incial para utilizar um repositório enterprise com o Nexus.

Apesar de trabalhar com o JFrog Artifactory como enterprise repository, gostei bastante do que vi ao utilizar o Nexus. Altamente recomendado.

[Referências]
Vídeo tutorial básico sobre uso do Nexus.
Maven pom reference.

Groovy & Grails Tutorials

junho 30, 2009

Estes dias encontrei um site agregador de tutoriais sobre Groovy e Grails bem bacana! Chamado Grails & Groovy Tutorials.

Apesar de ser bem tosco, o site (feito em Grails) agrega mais de 900 referências a tutoriais. Vale a pena conferiro.

SOA – Template para documentação para contrato de um Serviço.

junho 29, 2009

Estes tempos eu estava pensando em modelos de documentos que serviriam de template para representar serviços expostos num mundo SOA. Consegui identificar pelo menos dois tipos de documentos:

  • Documento de Contrato de Serviço e exposição;
  • Documento técnico de construção de serviços.

O primeiro apenas deve se preocupar com o contrato de um serviço, e o segundo é mais técnico e informa como um serviço deve ser construído tecnicamente, ou seja, qual a tecnologia utilizada para criá-lo. Um obrigatoriamente referencia o outro.

Estes também deveriam estar cercados por alguns documentos chave da Governança de SOA que padronizam a nomenclatura, granularidade de operações e grano de resposta enviada, modelo canônico, ciclo de vida, métricas gerias de SLAs e segurança, regras para composição de serviços e orquestração, interoperabilidade, políticas diversas, etc… Mas isto já é outro assunto.

Além do contrato, o primeiro documento também deve se preocupar com questões referentes a SLAs, segurança, métricas, tratamento sob falhas irrecuperáveis e também poderia conter até políticas para geração de alertas.

Deixo aqui para download a ideia para um template de especificação de contrato para um serviço, e um exemplo real de utilização deste documento. Pretendo amadurecê-lo durante o tempo, mas o que já fiz é bastante interessante.

Em um próximo post, criarei um template para documentar técnicamente um serviço, identificando  as informações necessárias para a criação deste e complementando em muito as informações do primeiro.


Seguir

Obtenha todo post novo entregue na sua caixa de entrada.