Caelum | Ensino e Inovação - Cursos de Java, Scrum, Ruby on Rails


Apache HTTP server e Tomcat, o HowTo fácil sem o mod_jk

Por Paulo Silveira em 25/09/06

Migramos os nossos sitemas do servidor compartilhado da locaweb para um servidor dedicado, porém enfrentamos o clássico problema: responder php e java na mesma porta de acordo coma URL. O nosso blog é gerenciado pelo wordpress, que precisa do PHP, já os nossos outros serviços precisam de Java 5 e tomcat 5.5.

Eu e o Guilherme Moreira somos apenas usuários comuns do linux, sem conhecimentos avançados. Para piorar estamos acostumados com o debian, e o servidor novo é um fedora. Tentamos fugir do apache http server (daqui em diante referido apenas como apache) e mais ainda do mod_jk.

A vantagem de usar o mod_jk é que o apache poderia servir o conteúdo estático das nossas web applications, mas já sabemos a algum tempo que o tomcat e o apache têm performance muito parecida quando estão servindo arquivos estáticos (htmls e imagens, por exemplo).

Corremos então atrás de alternativas mais simples, no meio do caminho encontramos soluções estranhas que não gostamos, e a melhor a que chegamos foi usar o mod_proxy.

Vamos então a um tutorial bem sucinto para essa instalação em qualquer distribuição linux, omitindo qualquer detalhe sobre descompactação e download de arquivos básicos. Caso você queira instalar tudo isso no windows, só que com o mod_jk, você pode acessar o excelente howto de 15 minutos que o Diego Plentz criou.

Baixe os fontes do Apache 2.2 e configure o build com o que precisamos (mod_rewrite para o wordpress):

./configure—prefix=/usr/local/apache —enable-rewrite \ —enable-shared=max \
—enable-so \
—enable-proxy

Você poderia adicionar outros módulos. Agora basta o make e o make install.

Vamos ao PHP, baixe o fonte e configure o build:

./configure—prefix=/usr/local \
—enable-xslt \
—with-apxs2=/usr/local/apache/bin/apxs \
—with-mysql=/usr/bin/mysql_config

make e o make install aqui também.

Pronto. Considere que o diretório /var/www/blog.caelum.com.br possui o nosso wordpress. No seu /usr/local/apache/conf (ou diretório similar) edite o httpd.conf e permita o acesso aos arquivos do diretório do wordpress:

<Directory /var/www/blog.caelum.com.br>
    Options FollowSymLinks
    AllowOverride All
    Order allow,deny
    Allow from all
</Directory>

Crie a associação de blog.caelum.com.br para o diretório /var/www/blog.caelum.com.br:

<VirtualHost *:80>
	ServerName blog.caelum.com.br
	DocumentRoot /var/www/blog.caelum.com.br
</VirtualHost>

Já, os outros sites que você quiser que sejam servidor pelo tomcat, você vai configurar o apache para agir como proxy, e delegar todas as requisições, e configuramos também para que ele tome o cuidado de reescrever alguns headers para que, por exemplo, não volte uma resposta com redirect para uma URL na porta 8080:

<VirtualHost *:80>
	ServerName www.caelum.com.br
	ServerAlias caelum.com.br www.caelum.com.br
	ProxyPass		/	http://caelum.com.br:8080/
	ProxyPassReverse	/	http://caelum.com.br:8080/
</VirtualHost>

Aí no seu server.xml do tomcat, basta direcionar o host caelum.com.br:

<Host name="caelum.com.br"
 	appBase="/xxx/yyy/webapps/caelum.com.br"
	unpackWARs="true" autoDeploy="true"
	xmlValidation="false" xmlNamespaceAware="false">
		<Alias>www.caelum.com.br</Alias>
</Host>

Pronto! Muito simples para até para que é apenas usuário do linux. E bem mais fácil que com o mod_jk! Agora tudo parece estar rodando corretamente, com apenas um probleminha ou outro. Fica aí a receita de bolo para quem precisar, qualquer sugestão é muito bem vinda!

  • Share/Bookmark

Livros: escolhendo a trindade do desenvolvedor Java

Por Guilherme Silveira em 22/09/06

Muitas pessoas costumam me perguntar se o livro X é bom, se o livro Y cobre bem Java EE, assim por diante. Alguns desses livros são super específicos de uma determinada tecnologia Java, como por exemplo Struts in Action que só aborda a versão 1.x.y do Struts. Em um curto espaço de tempo você vai acabar reciclando as folhas desses livros: ficarão desatualizados.

Na Caelum começamos a debater sobre quais livros importantes havíamos lidos e que ainda eram úteis, e queríamos muito que os outros que participavam da discussão também tivessem lido. A lista estava ficando muito grande quando resolvemos corta-la para apenas 3. Separamos então a trindade do desenvolvedor Java. O critério da escolha foi tentar achar uma intersecção dos livros considerados importantes por nós que todos saíssem satisfeitos. Repare que já são bem velhinhos:

  • Refactoring, Martin Fowler
  • Livro do cientista chefe da ThoughtWorks, a famosa empresa onde nosso amigo Carlos Villela trabalha. Um excelente catálogo das gambiarras (smells, mal cheiro) que acabam aparecendo no seu código e como você pode fazer para conserta-las de maneira sensata. Ok, você já sabia disso. Exemplos clássicos são o uso de herança apenas por preguiça, uso do switch em vez de polimorfismo, entre milhares. Uma pena o código não usar a conveção da Sun e estar lotado de underscores…

  • Effective Java, Joshua Bloch
  • Livro do ex bambambam da Sun (você pode encontrar o nome dele como @author das principais classes do Java SE), agora no Google. Aqui ele mostra como enfrentar os principais problemas e limitações da linguagem. Se você já programa a algum tempo sem dúvida alguma já passou por boa parte dos casos catalogados. Uma excelente leitura, além de simples pois cada caso é relatado em um texto bem curto. Entre os casos interessantes está o uso de factory methods, os problemas da herança e do protected, uso de coleções, objetos imutáveis e serialização.

  • Design Patterns, Erich Gamma et al
  • O clichê dos clichês não poderia estar fora da lista! Livro do atual líder do projeto Eclipse, entre outros. Compre! E ao contrário do que muitos fazem não saia lendo o catálogo dos patterns decorando-os, concentre-se em ler toda a primeira parte onde eles revelam duas regras de ouro da programação orientada a objetos: “Evite herança, prefira composição” e “Programe voltado a interfaces e não à implementação“. Infelizmente perdi minha cópia hard-cover em inglês, faz uma falta imensa na empresa.

Apesar disso, todos eles revelam sinais de sua idade. No Design Patterns, muita gente já considera Singleton como um antipattern (como discutido neste post). Mais ainda, alguns vão além, dizendo que se você precisou utilizar um design pattern, é provável que a sua linguagem não tenha um nível de abstração suficiente para suprir as suas necessidades. Na minha opinião, uma crítica válida.

No Refactoring e no Effective Java, muita coisa já não ocorre mais das formas descritas devido ao Java 5 (em especial com o uso de generics e enumerações), além de que vários códigos podem ser escritos de uma forma mais elegante.

Claro que eu não deixo de ler alguns livros bem técnicos. Os últimos dois que gostei muito foram Lucene in Action e Enterprise Java Beans 5th edition (este último é excelente). Ao contrário dos livros JSF in action e do Core JSF, livros os quais não achei que foram escritos de maneira a prender a atenção do leitor: joga-se muita especificação e conceitos, além de que não há uma ordem de aprendizado, parece mais um guia de referência. Sinceramente, não faz sentido algum explicar todos os passos de uma requisição JSF sem antes mostrar concretamente o objetivo e uso da tecnologia.

Ficaram de fora da lista Programming Pearls, Practices of an Agile Developer, The Pragmatic Programmer, The Practice of Programming entre outros gigantes. E você, tem algum livro que considere imprescindível para os desenvolvedores Java? Tem uma outra formação para a sua trindade?

  • Share/Bookmark

Como não aprender Java e Orientação a Objetos: getters e setters

Por Paulo Silveira em 14/09/06

Muitas pessoas me perguntam “como aprender OO?”. Há várias maneiras de aprender OO, creio que não tenha uma melhor, mas sem dúvida alguma existem maneiras de não aprender.

Uma das práticas mais controversas que aprendemos no início é a geração indiscriminada de getters e setters. Os exemplos básicos de centenas de tutoriais java estão recheados com getters e setters da pior espécie: aqueles que não fazem sentido algum. Considere:

class Conta {
  double limite;
  double saldo;
}

Rapidamente os tutoriais explicam o private para o encapsulamento. Mas aí como acessar? Getters e setters nela!!

class Conta {
  private double limite;
  private double saldo;
  
  public double getSaldo() {
    return saldo;
  }
  
  public void setSaldo(double saldo) {
    this.saldo = saldo;
  }
  
  public double getLimite() {
    return limite;
  }
  
  public void setLimite(double limite) {
    this.limite = limite;
  }
}

Qual é o sentido desse código? Para que esse setSaldo? e esse setLimite? e o getLimite? Você vai usar esse métodos? Nunca crie um getter ou setter sem sentir uma real necessidade por ele. Isso é uma regra para qualquer método, mas particularmente os getters e setters são campeões: muitos deles nunca serão invocados, e grande parte do restante poderia ser substituído por métodos de negócios. Códigos do tipo conta.setSaldo(conta.getSaldo() + 100) se espalharão por todo seu código. Segue então a nossa classe Conta reformulada de acordo com essa necessidade:

class Conta {
  private double saldo;
  private double limite;

  public Conta(double limite) {
    this.limite = limite;
  }

  public void deposita (double x) {
    this.saldo += x;
  }

  public void saca(double x) {
    if(this.saldo + this.limite >= x) {
      this.saldo -= x;
    }
    throw new IllegalArgumentException("estourou limite!");
  }

  public double getSaldo() {
    return this.saldo;
  }
}

E nem estamos falando de test driven development! Testando a classe Conta rapidamente você perceberia que alguns dos getters e setters anteriores não têm uso algum, e logo sentiria falta de alguns métodos mais voltados a lógica de negócio da sua aplicação, como o saca e o deposita acima. Esse exemplo é muito trivial, mas você pode encontrar por aí muitas classes que não tem a cara de um java bean que expõem atributos como Connection, Thread , etc, sem necessidade alguma!

Existem sem dúvida práticas piores: utilização de ids para relacionar os objetos, arrays não encapsuladas como estruturas, código fortemente baseado em comparação de Strings hardcoded (que o Guilherme Silveira sarcasticamente carinhosamente batizou de POS, ou programação orientada a strings), milhares de métodos estáticos, entre outros. Todas essas práticas são muito comuns quando estamos começando a quebrar o paradigma procedural, e confesso já ter sido um grande praticante de muitas delas. Um professor sempre me disse que você só vai utilizar bem o paradigma da orientação a objetos depois de errar muito.

O Phillip Calçado tem um artigo simplesmente incrível que de certa forma aborda esse tema: classes fantoches (puppets). Uma classe fantoche é a que não possui responsabilidade alguma, a não ser carregar um punhado de atributos! Onde está a orientação a objetos? Uma grande quantidade de classes fantoches são geradas quando fazemos Value Objects, entidades do hibernate, entre outros.

Mas o que colocar nas minhas entidades do hibernate e nos meus VOs além de getters e setters?“. Antes de tudo, verifique se você realmente precisa desses getters e setters. Para que um setID na sua chave primária se o seu framework vai utilizar reflection ou manipulação de bytecode para pegar o atributo privado, ou se você pode passá-la pelo construtor? Sobre value objects, você realmente precisa dos seus setters? Em muitos casos VOs são criados apenas para expor os dados, e não há necessidade alguma para os setters… bastando um bom construtor!

No hibernate costumo colocar alguns métodos de negócio, algo parecido como na classe Conta. Minhas entidades possuem uma certa responsabilidade, em especial as que dizem respeito aos atributos pertencentes a elas.

Em uma próxima oportunidade falarei de outro vício muito citado pela literatura básica: herança.

Para quem ainda está aprendendo, a apostila da caelum de java e orientação a objetos tem algumas dessas discussões e procura ensinar OO comentando sempre dessas más práticas. E em breve teremos uma versão 2.0 desse material.

  • Share/Bookmark

Você acredita no seu código?

Por Guilherme Silveira em 08/09/06

Como você faz para saber que seu software funcionou?

É impressionante perceber que estamos quase na primavera de 2006 e é tão fácil encontrar aplicações e bibliotecas sem uma única linha de código de teste.

Os problemas que surgem são os clássicos da época do cartão perfurado:

  • alterou uma coisa aqui, resultou em um bug ali
  • o responsável vai lançar um update novo do projeto mas não faz idéia se o que foi alterado está funcionando ou não
  • o “testador” verifica manualmente aquilo que foi alterado, mas acaba esquecendo de testar alguns itens
  • um bug gravíssimo foi encontrado portanto o gerente lança o projeto no meio de uma modificação. Resultado: código que não foi testado está em produção
  • conversa entre o usuário final e o desenvolvedor: usuário: Isso aqui não está funcionando, programador: na minha máquina funcionou

A última frase, por mais natural que pareça, é uma das coisas mais estranhas que existem na área. O programador foi contratado para o sistema funcionar em determinada arquitetura/so/etc, portanto enquanto o sistema não funcionar na máquina do seu cliente, não faz sentido dizer que ela funciona.

Hoje em dia trabalhamos com o desenvolvimento voltados a testes – quase todos os tipos deles – automatizados. Tanto na Caelum, quanto nos projetos open source que participamos. No XStream, por exemplo, nada pode ser comitado sem testes, e se for um novo recurso você também precisa comitar um pequeno tutorial, para que nada passe como “feature avançada não documentada”.

Isto é, usando ferramentas como o JUnit, Cargo, FIT e Fitnesse além do controlador VRaptor, o servidor Jetty, a biblioteca Hibernate e de bancos como o HSQLDB e o Derby, é possível criar diversos ambientes de teste, produzindo os seguintes resultados:

Integração contínua

  • Suas alterações são feitas e você efetua o commit para o controle de versão (cvs, subversion etc)
  • O sistema de integração contínua percebe o commit e, cerca de 1 minuto depois, efetua o processo de build
  • Ferramentas: BeetleJuice, CruiseControl ou até mesmo o TeamCity

Processo de build

  1. Baixa a última versão do controle de versão (checkout)
  2. Compila o código fonte
  3. Executa os testes unitários, verificando que suas classes funcionam sozinhas como deveriam, usando JUnit
  4. Levanta um servidor web embutido (jetty) usando o cargo
  5. Levanta um banco de dados embutido (hsqldb)
  6. Executa os testes de integração e aceite, onde o código html e os cliques que o cliente fazem são simulados, usando JUnit e VRapor
  7. Levanta um servidor web igual ao de produção (websphere, por exemplo), usando o cargo
  8. Levanta um banco de dados igual ao de produção (oracle, por exemplo)
  9. Executa os testes de integração e aceite na réplica da produção

Se algum desses passos falhar, o sistema notifica o time e o responsavel por quebrar a integração. Note que a integração é continua, isto é, ela percebe os commits, e não espaçada – sendo rodada uma vez por dia.

Nem sempre atingimos 100% de code coverage, dado que podemos extrair usando o Clover Report, mas saber que 70% ou mais do seu código foi testado e funciona como antigamente ajuda a efetuar modificações no seu código.

Sinceramente, não dá para confiar mais em código não testado automaticamente. Quais são as garantias que você tem que amanhã, aquilo que funcionava hoje não deixará de funcionar?

Lembro de quando o Carlos Villela falava de fazer todas essas etapas de teste e build “complexos” e achava coisa a mais. É impressionante como após fazer isso uma vez você não consegue mais viver sem ela.

Para aqueles que se motivaram a testar, divirtam-se, para os outros, boa sorte.

  • Share/Bookmark

Ensinando o que é o hashCode

Por Paulo Silveira em 04/09/06

Sem dúvida um dos pontos mais difíceis para quem está fazendo um curso inicial de java é entender hashCode, em especial para quem nunca viu estruturas de dados além de filas e pilhas, isso porque o desenvolvedor já está bem confuso em ter seu primeiro contato com a API das coleções, sintaxe do generics e uso extensivo de interfaces.

Aqui vou mostrar como costumo fazer essa explicação, e depois sugerir uma grande melhoria.

Costumo usar o exemplo de pesquisar por uma String dentro de uma lista de Strings. A busca seqüencial vai fazer até lista.size() comparações, o que pode ser muito grande. Então como diminuir esse número de comparações? Espalhando nossas Strings de acordo com algum critério, e colocando-as dentro de uma tabela de espalhamento (hashtable, hash=espalhar).

Mas como espalhá-las? Podemos classificar essas Strings de acordo com a sua primeira letra em uma array de listas (List<String>[]): strings[65] teria uma lista de Strings que começam com a letra A, e assim por diante. O problema é que, por exemplo, podemos ter todas nossas strings começando com A gerando a quantidade máxima de colisões em uma mesma lista! Daqui podemos elaborar a idéia para que as Strings sejam indexadas por um critério mais interessante. Por exemplo, multiplicar o valor inteiro de cada char: paulo valeria p*a*u*l*o, que é 112*97*117*108*111 = -1942066240 (explodiu Integer.MAX_VALUE). Esse número pode ser muito grande ou não nos servir por algum motivo, então nosso HashMap vai limitar o tamanho da array, e fazer algo como -1942066240 % array.length para saber em que bucket colocar esse ítem.

E continuo nessa linha de pensamento para explicar o contrato do método hashCode, realocação do tamanho da tabela de espalhamento e que isso tudo é utilizado na implementação de arrays associativas em algumas linguagens.

Os principais problemas desse exemplo:

  • Alguns desenvolvedores conhecem um pouco de árvores binárias, e querem usá-las. O problema é que tabelas de espalhamento são muito mais rápidas na pesquisa, além de que usar árvores binárias necessita de um critério de comparação entre os objetos, o que não é necessário em espalhamentos;
  • Gerar um int a partir de uma String atrapalha bastante. Repare a quantidade de passos que precisei passar: transformação de cada char em seu valor numérico da tabela unicode, multiplicar tudo, fazer mod, etc. Seria melhor se o objeto em questão já tivesse alguma relação numérica, e que não precisássemos fazer contas em cima de seus valores;
  • Pessoalmente sinto que poderia ser melhor.

Um amigo analista que trabalha no asset management de um grande banco brasileiro conversava comigo quando surgiu a necessidade dele encontrar dois arquivos de conteúdo idêntico que estavam no mesmo diretório. O problema é que esse diretório tem mais de 10 mil arquivos! Ele mesmo sugeriu fazer uma comparação primeiramente ordenando os arquivos por tamanho, e só vale comparar byte a byte os arquivos que possuírem o mesmo tamanho, os outros obviamente são diferentes entre si. Disse então para ele que esse era um excelente exemplo de tabelas de espalhamento, e que ele não precisaria nem mesmo ordenar os arquivos por tamanho, bastando separá-los!

Repare que esse meu amigo não é programador, mas todo engenheiro e administrador que trabalha em banco acaba sujando suas mãos com centenas (as vezes milhares) de linhas de VB dentro de funções excel. Isso mostra como a idéia de indexação e hash são muito simples e intuitivas, às vezes o que atrapalha é o professor :) .

Vamos ver como ficaria ensinar com esse exemplo: o contrato do método hashCode diz que:

se a.equals(b) então a.hashCode() == b.hashCode()

Pensando no problema da comparação dos arquivos, podemos falar:

fileA é equivalente a fileB então tamanho de fileA == tamanho de fileB

Colocando em java:

se fileA.equals(fileB) então fileA.length() == fileB.length()

Reescrevendo hashCode para espalhar de acordo com esse nosso critério, teríamos finalmente:

se fileA.equals(fileB) então fileA.hashCode() == fileB.hashCode()

Muito mais simples e direto que o exemplo da String! Aqui o debate poderia prosseguir em encontrar um hashCode que espalhasse melhor os arquivos, e o assunto cairia em checksums… mais perfeito impossível!

Vale lembrar que eu estou falando aqui de uma classe File imaginária, pois a java.io.File não leva em consideração o conteúdo do arquivo para o equals nem para o hashCode, ela usa apenas o abstract pathname em consideração.

  • Share/Bookmark



Caelum | Ensino e Inovação
São Paulo: Rua Vergueiro, 3185, cj. 87, próximo ao Metrô Vila Mariana   |   Tel. (11) 5571-2751
Rio de Janeiro: Rua Senador Dantas, 80, cj. 307/308 - Centro   |   Tel. (21) 2220-4156 ou 2297-0033
Brasília: SCS Qd. 8 Bl. B-50, Sala 521 - Ed. Venâncio 2000   |   Tel. (61) 3039-4222