segunda-feira, 19 de novembro de 2012

Log de Auditoria - JPA + Spring

Vira e volta, aparecem sistemas onde é necessário fazer um esquema de log de auditoria para uma tabela ou várias.

Montei um esquema que pode ser facilmente extendido para outras tabelas do sistema, para fazê-lo, basta seguir os passos abaixo:

1- Criar um enum para representar as ações de banco:
public enum TransactionType {
    CREATE, UPDATE, DELETE;
}


2- Criar a entidade de log de auditoria:
@Entity
@Table(name = "log_trace")
@AttributeOverride(name = "id", column = @Column(name = "log_trace_id"))
public class LogTrace extends BaseEntity {

    @Column(name = "transaction_type", nullable = false)
    @Enumerated(EnumType.STRING)
    private TransactionType transactionType;

    @Column(name = "entity_name", nullable = false)
    private String entityName;

    @Column(name = "registry_id", nullable = false)
    private Long registryId;

    @Column(name = "operation_date", nullable = false)
    @Temporal(TemporalType.TIMESTAMP)
    private Date operationDate;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "executed_by", nullable = false)
    private User executedBy;

...getters e setters...
}


3- Criar o repositório (DAO) e o serviço como está sendo feito no restante do sistema;

4- Criar o listener que será disparado pelo hibernate:
public class LogTraceListener {

    @Autowired
    LogTraceService logTraceService;


    @Autowired
    UserService userService;

    @PostRemove
    void postDelete(BaseEntity e) {
        createLog(TransactionType.DELETE, e);
    }

    @PostPersist
    void postPersist(BaseEntity e) {
        createLog(TransactionType.CREATE, e);
    }

    @PostUpdate
    void postUpdate(BaseEntity e) {
        createLog(TransactionType.UPDATE, e);
    }

    private void createLog(TransactionType transactionType, BaseEntity e) {
        /*
         * OBSERVAÇÃO 1.
         */
        if (logTraceService == null) {
            ApplicationContext ctx = ContextLoader.getCurrentWebApplicationContext();

            logTraceService = (LogTraceService) ctx.getBean("logTraceService");
        }

        /*
         * OBSERVAÇÃO 2.
         */
        User user = userService.getAuthenticatedUser();

        LogTrace logTrace = new LogTrace();
        /*
         * OBSERVAÇÃO 3.
         */
        String entityName = e.getClass().getAnnotation(Table.class).name();
        if (entityName == null || entityName.isEmpty()) {
            entityName = e.getClass().getSimpleName();
        }

        logTrace.setTransactionType(transactionType);
        logTrace.setEntityName(entityName);
        logTrace.setRegistryId(e.getId());
        logTrace.setExecutedBy(user);
        logTrace.setOperationDate(new Date());

        logTraceService.save(logTrace);
    }
}


Observações:
1- O listener é disparado pelo mecanismo JPA. Uma vez que a classe não foi instanciada pelo Spring, as dependências não são injetadas e para resolver isso, podemos usar aspéctos (AOP) ou uma abordagem mais simples como a feita acima;
2- A lógica de recuperação do usuário logado (via consulta ou busca na sessão) está no serviço neste caso;
3- Alguns projetos adotam o uso da anotação @Table, neste listener, coloquei uma lógica que abrange esta abordagem (como principal) e se não for o caso, o campo receberá o nome da entidade, por exemplo, "PedidoInterno", ao invés de "pedido_interno" (que é a forma como os bancos normalmente são modelados. Veja que nossa "LogTrace" teria o campo salvo como "log_trace";

5- Anotar entidades que devem ser auditadas:
As entidades que devem ser auditadas recebem a anotação @EntityListeners(value = LogTraceListener.class). Para melhorar a leitura da classe, indico colocar esta anotação logo após a anotação @Entity ou @Table(name = "minha_entidade").

Com isso, sempre que uma das operações monitoradas pelo listener acontecer, são as anotações que estão sobre os métodos: @PostPersist, @PostUpdate e @PostRemove, um registro será gerado na tabela de log de auditoria.

Para que outra tabela/entidade seja autitada, basta repetir o passo 5 (incluir a anotação na entidade).

NOTA: Normalmente as entidades de um sistema extendem uma entidade base ou implementam uma interface para que um padrão "mínimo" seja seguido. Este exemplo foi construído considerando este caso, ou seja, as entidades extendem BaseEntity.

segunda-feira, 17 de setembro de 2012

App web Java + Maven não roda no ecplise

Imagine que você desenvolveu uma aplicação java com maven e versionou ela em algum repositório da vida, por exemplo, github, e ao instalá-la em outro computador ela apresenta a seguinte mensagem quando o servidor sobe:

Error configuring application listener of class org.springframework.web.context.ContextLoaderListener

Consultando este problema na internet achei a seguinte solução:
  1. Abre as propriedades do projeto (botão direito no projeto e propriedades); 
  2. Selecione "Deployment Assembly";
  3. Clique em "Add...";
  4. Selecione "Java Build Path Entries" e clique em "Next";
  5. Selecione "Maven Dependencies" e clique "Finish";
Reinicie seu servidor tomcat e veja que agora funciona.

Essas dicas foram retiradas do post 6210757 - java-lang-classnotfoundexception-org-springframework-web-context-contextloaderl no stackoverflow.com

quarta-feira, 18 de julho de 2012

JSF - Página de erro após sessão expirada?

Eu estava pegando sempre uma página de erro após muito tempo de inatividade em uma aplicação JSF ou quando reiniciava o servidor e clicava em algum link na página.

Para resolver esse problema chato, usei as seguintes linhas no web.xml da aplicação.

<error-page>
    <exception-type>
        javax.faces.application.ViewExpiredException
    </exception-type>
    <location>
        /index.html
    </location>
</error-page>


Com essas linhas no web.xml a aplicação ao invés de exibir a página de erro quando a view estiver expirada, vai redirecionar o usuário para a página indicada.

JSF - URL não muda após redirect?

Em situações que um método retorna a url de redirect da sua página, como por exemplo:

public String listar() {
    return "/pages/list.xhtml";
}


E o JSF não muda a URL na barra do browser, recomendo a inclusão da seguinte string após a url:  + "?faces-redirect=true"

ficando:

public String listar() {
    return "/pages/list.xhtml"  + "?faces-redirect=true";
}


Fazendo isso, a URL sempre vai mudar. ;-)

domingo, 15 de abril de 2012

JSF 2.0 + Spring - Injeção de Dependência (DI)

Estou desenvolvendo uma aplicação utilizando JSF 2.0, Hibernate, Spring 3, etc...

No caso, procurei utilizar anotações e acabei cometendo um equívoco na escolha das anotações que a principio, não gerou problema, porém, quando testei multiplos acessos à aplicação, os problemas começaram a aparecer, como por exemplo, um usuário invalidando a sessão do outro.

O ponto é que eu estava usando a anotação @Component("nomeBean") nos beans gerenciados e @Autowired para fazer o trabalho de DI (anotações do Spring).

Pesquisando para resolver o problema, cheguei ao seguinte resultado:

- Ao invés de:
@Component("nomeBean")

- Use:
@ManagedBean(name="nomeBean")

- e ao invés de:
@Autowired

- Use:
@ManagedProperty(value="#{nomeBeanInjetar}")


Utilizarei como exemplo uma classe usuário recebendo a injeção de um UsuarioDAO.

- Implentação do UsuarioDAO que deverá ser injetado no objeto usuarioController, que é meu managed bean:

@Component("usuarioDAO")
public class UsuarioDAO implements IUsuarioDAO {
...

- Classe que receberá a injeção:

@ManagedBean(name="usuarioController")
@SessionScoped
public class UsuarioController {

@ManagedProperty(value="#{usuarioDAO}")
private IUsuarioDAO usuarioDao;

public void setUsuarioDAO(IUsuarioDAO usuarioDAO) {
this.usuarioDAO = usuarioDAO;
}
...


Note que na abordagem acima estou usando anotações do JSF (@ManagedBean e @ManagedProperty) e anotações do Spring.

A ideia é a seguinte:
- Criamos os objetos que serão reutilizados mais adiante sendo injetados em nossos managed beans, esses objetos devem ser marcados como componentes e nesse momento, devemos dar um nome a ele, nesse processo, o Spring vai criar este objeto em memória e deixá-lo pronto para ser injetado quando for chamado;
- O segundo passo é no nosso managed bean que devemos identificar a propriedade com a anotação @ManagedProperty e informar qual será o objeto que deverá ser injetado usando o mesmo nome dado anteriormente;

Bom, seguindo o padrão de posts rápidos e dirétos, basicamente foi isso que me ajudou bastante.

OBS.:
1. Quando utilizada a anotação @ManagedProperty, o atributo deve possuir um setter associado a ele, como no exemplo acima;

2. Quando ainda não tinha notado que o erro era este, procurei coisas estáticas na aplicação e por ai vai, então, fica a dica;

Espero ter ajudado e até a próxima.

sexta-feira, 30 de março de 2012

Alguns comandos úteis para utilizar MAVEN

Abaixo vou listar algumas linhas de comando que podem ajudar no desenvolvimento de aplicações que usam MAVEN:

- Criar uma aplicação:
$ mvn archetype:generate -DgroupId=br.com.meusistema -DartifactId=AppExemplo -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false 

Na primeira vez que este processo é executado, as coisas demoram um pouco por que o maven vai baixar meia internet no repositório local que ele cria na máquina.

Terminado o processo, você verá que o maven criou uma pasta com o nome que foi utilizado no artifactId, no nosso exemplo, "AppExemplo"

Depois disso, para que este projeto possa ser aberto no eclipse fácil, fácil, o comando é:
$ cd AppExemplo
E dentro do diretório da aplicação:
$ mvn eclipse:clean 
$ mvn eclipse:eclipse 
ou
$ mvn eclipse:m2eclipse 

Na primeira vez deve demorar um pouco também.

Depois, basta importar o projeto no eclipse e seguir a vida.

Estou disponibilizando abaixo uma lista de comandos que normalmente uso.

$ mvn clean
$ mvn compile
$ mvn install

Pretendo em uma hora com calma editar este post e incluir mais detalhes sobre cada operação descrita e incluir mais algumas que costumo usar.

terça-feira, 28 de fevereiro de 2012

Motorola defy rooting android 2.2.1

Eu estava pesquisando na net formas de obter acesso root no meu defy com android 2.2.1 e achei o seguinte post:
- [Rom] Cyanogenmod 7 – 2.3.7 Defy

Este foi o post mais completo, fácil e seguro que achei de realizar o processo, inclusive, para quem quiser fazer a atualização para o android 2.3.7 custom ROM cm7, pra mim funcionou muito bem e o aparelho fica muito, mas muito melhor mesmo.

Para evitar o risco dos links do post do amigo quebrarem, eu baixei aqui os arquivos necessários para fazer o root e o boot de configuração:
- GingerBreak
- SndInitDefy-1.4.2

Obs.:
- Para baixar os arquivos, clique com o botão direito no link e em seguida, “Salvar link Como”;
- Retire a extenção GIF antes de utilizar os arquivos e conserve a extenção apk;
- Para o rooting, use apenas o GingerBreak, o outro link será necessário apenas para iniciar o processo de troca de ROM;

Para fazer o rooting é muito simples:
1. Copie os arquivos acima para seu SDCard (na raiz pra ficar mais fácil);
2. Vá em configurações, Aplicativos, Desenvolvimento e habilite a opção Depuração USB;
3. Com algum programa de exploração de pastas, como o ASTRO, por exemplo, localize o GingerBreak e execute-o;
4. Após a instalação, abra o programa e clique no primeiro botão (Root Device);

Pronto, o processo de rooting vai se conduzir sozinho e reiniciará o aparelho.

Eu aconselho que o vídeo do post mencionado acima seja visto, o artigo do camaradinha é muito bom e completo.

OBS.: Para quem quiser instalar o CM7 seguindo o tutorial acima, após a instalação, use a banda da Claro mencionada no tutorial, eu sou usuário VIVO e pra mim funcionou perfeitamente com aquela opção. A banda “Viva” não funcionou muito bem.
Espero ter ajudado e fiquem a vontade pra comentar o post.

CSS media type

Ao criar uma página HTML, é possível fazer com que o visual da página seja diferente para diversos tipos de dispositivos de apresentação.

Uma forma bem simples de fazer isso é usando o parâmetro media ao criar o CSS na própria página ou no import de um arquivo CSS.

Abaixo dois exemplos básicos:
<style type="text/css" media="print">
   body {
     Background-color: #FFFFFF;
   }
</style>
<style type="text/css" media="screen">
   body {
     Background-color: #CCCCCC;
   }
</style>
Isso fará com que o fundo da página seja branca na página de impressa (OBS.: Os browsers costumam vir por padrão com a opção de impressão de imagens e cores de fundo desabilitada, ou seja, se outra cor fosse colocada, com essa configuração padrão, o fundo permaneceria branco) e na tela o fundo será um tom de cinza.

Quando não incluímos a opção media na criação do CSS, por padrão, seria como se tivessemos atribuido o valor “all” para esta propriedade.

Para importar um arquivo de estilos, o padrão é o mesmo:
<link rel="stylesheet" href="css/impressao.css" media="print" />
<link rel="stylesheet" href="css/tela.css" media="screen" />
Essa é a parte tranquila e básica da história, agora pode ocorrer a necessidade de fazer com que um HTML tenha que aparecer na tela de um jeito, ser impresso de outro e ainda ser enviado por email de outro jeito.

Este terceiro ponto pode ser um problema.

Navegando por sites a procura de coisas sobre o assunto encontrei o seguinte link que descreve um pouco a compatibilidade de cada programa de email com folhas de estilos:

Guide to CSS support in email

Para alguns pode ser bastante desanimador ao ver isso, e uma solução simples de implementar que achei de fato é descepcionante, mas, funcionou no caso de páginas JSP.

No meu caso, criei uma página JSP que recebe um parâmetro informando se ela será usada em um email ou não e ai, quando ela deverá ser utilizada no email ao invés de importar uma folha de estilos eu incluo o estilo diretamente no objeto, conforme exemplo abaixo:
... Código
 <td <%= email ? "style='text-align: left;'" : "class='tdDiferente'"%> >
... Código
É uma forma bem chata de tratar isso, mas no meu caso foi eficiênte, caso alguém tenha mais sugestões, eu agradeço o envio.

Assinando um JAR (JAVA WEB START)

Toda vez que criamos uma aplicação Java Web Start e precisamos que esta aplicação tenha alguns acessos a máquina cliente, é necessário incluir no arquivo JNLP a seguinte instrução:
<security>
  <all-permissions/>
</security>
O problema é que feito isso, é necessário assinar os jars que estão na lista que deve ser entregue a máquina cliente, abaixo criei um tutorial bem simples de como fazer isso.
- Primeiro – no diretório onde estão os jars, digite a linha de comando que segue:
keytool -genkey -alias macielbombonato -keystore macielbombonato.cert
- Escolha a senha (minimo de 6 caracteres):
Enter keystore password:
Re-enter new password:
- Responda o questionário:
What is your first and last name?
[Unknown]: XXX
What is the name of your organizational unit?
[Unknown]: XXX
What is the name of your organization?
[Unknown]: XXX
What is the name of your City or Locality?
[Unknown]: XXX
What is the name of your State or Province?
[Unknown]: XX
What is the two-letter country code for this unit?
[Unknown]: XX
Is CN=XXX, OU=XXX, O=XXX, L=XXX, ST=XX, C=XX correct?
[no]: yes
Esses passos acima vão criar um certificado na pasta onde os jars estão, agora é necessário assinar cada um dos jars com esse certificado, para isso, basta utilizar a linha de comando abaixo:
jarsigner -verbose -keystore macielbombonato.cert -storepass 123456 -keypass 123456 
MeuJarAssinadoWebStart.jar macielbombonato
Caso não queira incluir as senhas nas linhas de comando para assinar os jars, basta não utilizar os parâmetros -storepass e -keypass, com isso, a senha será solicitada antes do jar ser assinado.

Dúvidas/Sugestões, por favor, comente este post.

Configure Proxy para subversion

Essa dica vale pra quem precisa configurar proxy para utilização do subversion no sistema operacional como um todo ou para a utilização do plugin do eclipse o subclipse.

Para quem estiver utilizando o sistema operacional Windows, um amigo achou na net a seguinte referencia: http://ykyuen.wordpress.com/2010/03/09/eclipse-configure-proxy-for-subclipse/

Abaixo irei colocar o texto retirado deste site para o caso do post algum dia sair do ar.

The Eclipse proxy setting does not apply on subclipse. So you cannot connect to a SVN repostory outside your firewall even you have set the proxy in Eclipse.
Luckily, i found the solution in MKVille Blog – Using Subclipse Behind a Proxy Server.
For Windows XP user
Open the C:\Documents and Settings\\Application Data\Subversion\servers
Configure the http-proxy-host and http-proxy-port settings under the [global] tag

For Windows 7 user
Open the C:\Users\\AppData\Roaming\Subversion\servers
Configure the http-proxy-host and http-proxy-port settings under the [global] tag

Done =)

Agora a pergunta, porque estou reescrevendo este post?

Para dar a dica para quem usa mac (ou linux).

No caso, basta realizar basicamente os mesmos passos que o autor do post original comentou, porém, usuários de mac devem usar o seguinte endereço:

/Users/[nome_do_usuario]/.subversion

Agora só preciso arrumar um tempo pra montar uns scripts pra ativar e desativar o proxy para quem é consultor e vai e vem para clientes.

Espero que este post ajude mais alguém.

Utilizando Git (Versionador) local

Versionando um diretório com git.
Dentro do diretório executar o comando:
$ git init
E depois:
$ git status
– Status do branch selecionado
$ git branch
– Exibe todos os branchs e marca com um asterisco o que está selecionado.
$ git add .
– . (ponto) adiciona todos arquivos que estão pendentes
– [nome do arquivo] adiciona um arquivo específico
$ git checkout -b [nome do branch]
– Criar um branch
$ git commit -am “[mensagem]“
– Commit das alterações
$ git checkout [nome do branch]
- Altera do branch atual para o branch mencionado

– Para "pegar" as alterações do branch master para outro branch qualquer:
$ git checkout [nome do branch]
$ git rebase master
– Para "mandar" as informações de um branch qualquer para o master:
$ git checkout master
$ git merge [nome do branch]
+ No caso do branch master ser remoto:
– Para atualizar seu master local:
$ git checkout master
$ git pull origin master
– Para atualizar o branch master remoto com suas alterações locais:
$ git checkout master
$ git push origin master

Editado (25/04/2012) - Inclusão de repositório remoto.

Os passos acima foram seguidos e o projeto está sendo versionado localmente apenas, porém, em dado momento eis que surge um servidor git para que o projeto seja versionado, neste caso, o git oferece recursos bem simples de alteração do repositório que o projeto deve apontar, para isso basta:

$ git remote add origin https://enderecodoseuservidor/aplicacao.git # Faz com que o host remoto git do seu projeto passe a ser este indicado na url
$ git push -u origin master # Para enviar os fontes para o branch master remoto pela primeira vez

Instalação/Preparação do Diaspora para desenvolvimento

Primeiros passos, preparando a casa:

Instalação do rvm:
bash < .rvmrc
cd .
rvm gemset create ‘diaspora’
cd ..
cd diaspora
Confirme as mensagens que aparecem.
bundle install
Tire a palavra exemplo do nome do arquivo database.yml, certifique-se de que as configurações de banco de dados que estão no arquivo conferem com as da sua máquina.

Tire a palavra exemplo do nome do arquivo app_config.yml.

No terminal execute o comando:
rake db:create db:migrate
Pronto!

Agora é só personalizar a aplicação.

Instalação Redis MacOS X e Linux em 30s

Acesse: http://redis.io/

Baixe o último release em algum local da sua máquina.

Descompacte e via terminal execute o seguite comando na pasta do redis.
sudo make install
Após a conclusão da instalação, basta executar o comando:
redis-server
Pronto!

Pré requisitos:
MacOS X: Xcode
Debian/Ubuntu: Pacote build-essential (sudo apt-get install build-essential).