Thursday, August 15, 2013

Problemas c/ jar assinado pós upgrade do Java 7: invalid SHA1 signature file digest

Durante a migração de um projeto, ou melhor atualização, do Java 6 para o 7, encontrei alguns problemas relacionados certificação dos jars. Um módulo desse projeto é em Swing, instalado no cliente via Java Web Start. A aplicação swing é formada por vários jars, que por sua vez devem ser assinados com o certificado definido pela empresa. Depois de atualizar o workspace e o build, na inicialização da aplicação o Web Start lançou uma java.io.IOException: invalid SHA1 signature file digest for...

O jarsigner mudou no Java 7, o algoritmo default da assinatura passou a ser o SHA-256. No Java 6 era o SHA-1. Esse foi o motivo do erro, na verdade existiam alguns jars usando as duas assinaturas, uma para SHA1 e outra SHA256 (é possível visualizar isso no Manifest.MF).

A solução do problema foi deixar explicito no jarsigner, o uso do algoritmo correto, através da propriedade -digestalg SHA1. Veja:
1
$ jarsigner -keystore .... -digestalg SHA1

No ANT, é possível definir a propriedade na task signjar:
1
2
3
<signjar ... digestalg="SHA1">
  ...
</signjar>

E por fim, como indicar essa propriedade no Maven:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<!-- omiti o restante do pom.xml -->
<build>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-jarsigner-plugin</artifactId>
      <version>1.2</version>
      <executions>
        <execution>
          <id>sign</id>
          <phase>package</phase>
          <goals>
            <goal>sign</goal>
          </goals>
        </execution>
      </executions>
 
      <configuration>
        <keystore>...</keystore>
        <alias>...</alias>
        <storepass>...</storepass>
        <keypass>...</keypass>
  <digestalg>SHA1</digestalg>
      </configuration>
    </plugin>
  </plugins>
</build>
...

Em outro post demonstro como usar o jarsigner pelo Maven.

@edermag

Thursday, August 08, 2013

Como executar testes no JMeter (remoto) a partir do ANT

Dica útil para projetos aonde os builds são realizados pelo ANT. O projeto jmeter-ant-task define uma task customizada do ANT, para executar testes (stress / carga / ...) com o JMeter.

Na task jmeter existem diversos atributos utilizados para indicar qual é o diretório de instalação do JMeter, qual é o diretório com os planos de testes (arquivos jmx), qual diretório deve conter resultados dos testes devem ser armazenados e outros.

O trecho de build.xml a seguir demonstra como utilizar a task para executar o plano de testes MyTest.jmx. 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<project name="MyProject" basedir=".">
 
  <!-- define uma var que guarda a url de app qualquer -->
  <property name="app.url" value="htpp://localhost:8080/myapp" />
 
  <!-- vars utilizadas pelo jmeter -->
  <property name="jmeter-home" value="/opt/jmeter" />
  <property name="jmeter-lib-dir" value="${jmeter-home}/lib" />
  <property name="jmeter-results" value="/home/app/jmeter-results" />
  <property name="jmeter-tests" value="/home/app/jmeter-tests" />
 
  <!-- Carrega a task do JMeter -->
  <taskdef name="jmeter" classpathref="apache.ant.ext.lib.classpath"
    classname="org.programmerplanet.ant.taskdefs.jmeter.JMeterTask" />
   
  <target name="jmeter.run" depends="" >
    <echo>Acionando o JMeter local p/ os testes de stress</echo>
    <jmeter jmeterhome="${jmeter-home}" testplan="${jmeter-tests}/MyTest.jmx"
     resultlogdir="${jmeter-results}/jtl" jmeterlogfile="${jmeter-results}/log">
      
      <property name="app.url" value="${app.url}"/>
 
      <!-- passa argumentos p/ a vm do jmeter -->
      <jvmarg value="-Xincgc"/>
      <jvmarg value="-Xmx128m"/>
    </jmeter>
  </target>
 
  ...
</project>

No inicio do build são definidas variáveis utilizadas pelo o script, boa parte delas na task jmeter. Um detalhe sobre o plano de teste é que ele depende de uma variável que indica a url da aplicação, app.urlNote o uso da tag property, nela eu repasso a variável definida no build ANT, a ${app.url} com o endereço da aplicação. Dessa é possível enviar informações para o JMeter, sem manipular o arquivo jmeter.propertiesNa tag jvmarg você pode indicar parâmetros para a VM do JMeter.

O target a seguir simila a execução do Jmeter remoto, em outro servidor. Para executar o JMeter remoto, é necessário pelo menos dois nós (instâncias) do JMeter, uma cliente e outra servidor, veja mais na documentação do projeto. O próximo target aciona o JMeter cliente, na mesma máquina que o build está sendo executado, indicando a propriedade remote_hosts. Dessa forma a instância cliente irá acionar o JMeter servidor nesse endereço indicado.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
...
  <!-- host/ip para o jmeter remoto -->
  <property name="jmeter-host" value="192.168.1.18"/>
  <property name="jmeter-port" value="1099"/>
 
  <target name="jmeter.run.remote">
    <echo>Acionando o JMeter remoto p/ os testes de stress</echo>
    <jmeter jmeterhome="${jmeter-home}" jmeterlogfile="${jmeter-results}/log" resultlogdir="${jmeter-results}/jtl" runremote="true">
      <!-- outra forma de definir os planos de testes -->
      <testplans dir="test/jmeter/jmx" includes="MyTest.jmx"/>
 
      <!-- indica que essa property deve ser enviada ao jmeter server -->
      <property name="app.url" remote="true" value="${app.url}"/>
 
      <!-- indica a url do jmeter server -->
      <property name="remote_hosts" value="${jmeter-host}:${jmeter-port}"/>
    </jmeter>
  </target>
...

Note que a propriedade app.url foi marcada como remote="true", dessa forma o JMeter cliente repassa o valor para o JMeter servidor. Mais informações sobre essa task foram disponibilizadas na página do projeto.

http://twitter.com/edermag
http://www.yaw.com.br

Thursday, August 01, 2013

Overview das ferramentas do Spring para persistência de dados

Durante o desenvolvimento ou manutenção (principalmente) de um projeto de software, seja qual for o estilo, é sempre delicado lidar com os componentes de infra-estrutura responsáveis pela camada de persistência.

Vou descrever alternativas que o Spring Framework oferece para simplificar esse trabalho, em cenários variados, como o foco em base de dados relacional. Ou seja, um overview das ferramentas do Spring para trabalhar com os componentes de persistência.

Código legado, bem legado...

Projetos Java antigos, desenvolvidos a + de 8 anos, normalmente não utilizam uma solução ORM (Mapeamento Objeto Relacional). Nesses projetos é muito comum o uso de bibliotecas "caseiras", escritas in house, para otimizar o uso do JDBC.

O Spring JDBC é um modulo do Spring interessante para esse tipo de cenário. Com ele é possível reduzir consideravelmente o volume de código JDBC. O principal componente do Spring JDBC é o JdbcTemplate, ele disponibiliza uma série de métodos para operações CRUD, consultas e comandos em lote. Para tirar proveito do uso contextos, injeção de dependências e inversão de controle, faz todo o sentido trabalhar em conjunto com o Spring Bean. Dessa forma seria possível injetar a referência do JdbcTemplate nos componente DAO (pattern Data Access Object).

O código a seguir demonstra um fragmento do DAO que utiliza o JdbcTemplate para inserir/atualizar uma entidade (Mercadoria).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Component
public class MercadoriaDAO {
 
  //SQL
  private final static String INSERT_MERCADORIA = "INSERT INTO mercadoria (nome,descricao,preco,quantidade) VALUES (?,?,?,?)";
  private final static String UPDATE_MERCADORIA = "UPDATE mercadoria SET nome = ?, descricao = ?, preco = ?, quantidade = ? WHERE id = ?";
  private final static String GET_MERCADORIA_BY_ID = "SELECT * FROM mercadoria WHERE id = ?";
  private final static String GET_MERCADORIAS_BY_NOME = "SELECT * FROM mercadoria WHERE nome like ?";
   
  @Autowired
  private JdbcTemplate jdbcTemplate;
   
  public void save(Mercadoria m) {
    if (m.getId() == null) {
      jdbcTemplate.update(INSERT_MERCADORIA,
        new Object[]{ m.getNome(), m.getDescricao(), m.getPreco() });
    } else {
      jdbcTemplate.update(UPDATE_MERCADORIA,
        new Object[]{ m.getNome(), m.getDescricao(), m.getPreco(), m.getId() });
    }
  }
  ...
}

Além do método update também pode ser utilizado para realizar a remoção da entidade. No trecho de código a seguir coloco dois exemplos de consultas, utilizando JdbcTemplate. Note que na consulta utilizamos o componente RowMapper, o MercadoriaRowMapper. O RowMapper é utilizado pelos métodos query de JdbcTemplate, ele lê os dados do ResultSet e faz a transformação em uma instância de Mercadoria.

O método queryForObject é utilizado para retornar uma instância da entidade (ou null) com filtro por id, por exemplo. Enquanto o método query retorna uma lista de objetos que podem ser encontrados de acordo com o SQL.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
... //ainda em MercadoriaDAO
 
private class MercadoriaRowMapper implements RowMapper<Mercadoria> {
 
  public Mercadoria mapRow(ResultSet rs, int row) throws SQLException {
    int id = rs.getInt("id");
    String nome = rs.getString("nome");
    String descricao = rs.getString("descricao");
    double preco = rs.getDouble("preco");
  
    return new Mercadoria(id, nome, descricao, qtde, preco);
  }
}
 
public Mercadoria findById(Integer id) {
  return jdbcTemplate.queryForObject(GET_MERCADORIA_BY_ID,
    new Object[] { id }, new MercadoriaRowMapper());
}
 
public List<Mercadoria> getMercadoriasByNome(String nome) {
  return jdbcTemplate.query(GET_MERCADORIAS_BY_NOME,
    new Object[] { nome + "%" }, new MercadoriaRowMapper());
}
...

Outra estratégia, que não me agrada, seria fazer no DAO a Mercadoria uma herança para JdbcDaoSupport, e acessar o JdbcTemplate encapsulado nesse componente. Com Spring JDBC o código DAO pode ficar bem mais compacto. Para ilustrar isso, compare o DAO c/ JDBC puro e o DAO utilizando Spring JDBC. Veja também as configurações do Spring e o o pom.xml com as dependências para esses módulos.

Projetos com Hibernate

O Spring também oferece soluções para reduzir o esforço e agregar funcionalidades durante o desenvolvimento de projetos Java utilizando soluções ORM, como Hibernate ou JPA. O Spring ORM é outro módulo da suíte Spring, ele oferece funcionalidades para facilitar o uso de soluções baseadas em Mapeamento Objeto Relacional.

Em versões antigas do Hibernate, era trabalhoso manter a Session vinculada ao contexto de execução, por exemplo utilizar a mesma instância em diferentes DAOs dentro do mesmo fluxo de request. Por isso o Spring criou o HibernateTemplate, com a proposta de disponibilizar a Session corrente ao contexto de execução.

Mas a partir do Hibernate 3.0.1 com contextual sessions, isso não é mais necessário. Nas versões mais recentes do Spring é possível usar a Session no contexto de execução, além de centralizar as configurações e realizar a injeção da SessionFactory nos DAOs.

A seguir o código do DAO da Mercadoria com Hibernate:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@Component
public class MercadoriaDAO {
 
  @Autowired
  private SessionFactory sessionFactory;
   
  private final Session getCurrentSession(){
    return this.sessionFactory.getCurrentSession();
  }
   
  public void save(Mercadoria m) {
    if (m.getId() == null) {
      this.getCurrentSession().persist(m);
    } else {
      this.getCurrentSession().merge(m);
    }
  }
   
  public Mercadoria findById(Integer id) {
    return (Mercadoria) this.getCurrentSession().get(Mercadoria.class, id);
  }
   
  public List<Mercadoria> getMercadoriasByNome(String nome) {
    return this.getCurrentSession()
      .createQuery("from model.Mercadoria m where m.nome like ?")
      .setParameter(0, nome+"%")
      .list();
  }
  ...
}

O spring-config.xml a seguir centraliza no Spring as configurações com banco de dados e do Hibernate.  Nesse exemplo o banco de dados utilizado é o HSQLDB (local).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<?xml version="1.0" encoding="UTF-8"?>
       xsi:schemaLocation="
   
  <context:component-scan base-package="." />
   
  <!-- SessionFactory, DataSource, ... -->
 
  <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
    <property name="dataSource" ref="ds" />
    <property name="packagesToScan" value="model" />
   
    <property name="hibernateProperties">
      <props>
        <prop key="hibernate.dialect"> org.hibernate.dialect.HSQLDialect</prop>
        <prop key="hibernate.show_sql">true</prop>
        <prop key="hibernate.hbm2ddl.auto">create</prop>
      </props>
    </property>
  </bean>
   
  <bean id="ds" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <property name="driverClassName" value="org.hsqldb.jdbcDriver"/>
    <property name="url" value="jdbc:hsqldb:file:mercadoria"/>
    <property name="username" value="sa"/>
    <property name="password" value=""/>
  </bean>
  ...
</beans>

Projetos com JPA++

O Spring também oferece funcionalidades bem interessantes para projetos que utilizam a Java Persistence API (JPA), através do módulo Spring Data JPA. Através da interface JpaRepository o Spring define os métodos de consulta e CRUD. O desenvolvedor trabalha de forma alto-nível, criando uma interface de persistência, enquanto a implementação fica por conta do próprio Spring. Outro elemento do Spring Data JPA é a anotação @Query, responsável por informar consultas customizadas via JPAQL.

Para esse tipo de componente o Spring adota o pattern Repository, que abstrai uma coleção de objetos com o design mais próximo ao domínio da aplicação do que com o banco de dados. Veja como ficaria a interface MercadoriaRepository:
1
2
3
4
5
6
public interface MercadoriaRepository extends JpaRepository<Mercadoria, Integer> {
 
  @Query("select m from Mercadoria m where m.nome like ?1")
  List<Mercadoria> getMercadoriasByNome(String nome);
 
}

Uma vez que a interface foi definida, o Spring cria um proxy que implementa os métodos de persistência. Esse proxy será injetado em classe de negócio/controller pelo Spring. Veja o exemplo:
1
2
3
4
5
6
7
8
9
10
11
@Component
public class MercadoriaService {
 
  @Autowired
  private MercadoriaRepository repo;
   
  public void save(Mercadoria m) {
    m.save(m);
  }
  ...
}

Nesse exemplo demonstrei funcionalidades básicas da interface JpaRepository, mas existem outrs funcionalidades como o suporte a paginação da consulta SQL, veja esse exemplo. No github compartilhamos outros dois projetos que utilizam Spring Data JPA, uma aplicação web e outra desktop. Ambas fazem uso do JpaRepository.

Um pouco além: NoSQL

Na verdade esse módulo compõe o Spring Data, uma solução "guarda-chuva" com o objetivo de unificar e simplicar o armazenamento de dados em bancos relacionais e NoSQL. Abaixo desse projeto existe o módulo Spring Data MongoDB, responsável por abstrair o acesso ao MongoDB. Não é o proposito desse post abordar soluções NoSQL, mas para ter uma idéia o trecho de código a seguir demonstra repositório da Mercadoria (como Document) em versão MongoDB:
1
2
3
4
5
6
public interface MercadoriaRepository extends MongoRepository<User, String> {
   
  @Query("{ nome: ?0 }")
  List<User> getMercadoriasByNome(String nome);
   
}

Abaixo do Spring Data ainda existem sub-projetos para Neo4j, Apache Hadoop, REST e outros. É possível saber um pouco mais sobre essa tecnologia, em artigo introdutório sobre Spring Data no InfoQ Brasil.

Esse é apenas um resumo de algumas soluções oferecidas pelo Spring Framework para resolver questões relacionadas a persistência em projetos Java. Sou da turma que gosta do Spring e de Java EE. Acredito que tirando proveito das melhoras funcionalidades das duas stacks, aumentamos o nosso poder fogo e logo a possibilidade de desenvolver um projeto com sucesso.

http://twitter.com/edermag
http://www.yaw.com.br