Categoria Java

Sem reinventar a roda, cache de páginas com EhCache e ServletFilter

Não faz muito tempo, surgiu a necessidade de aumentar o desempenho de uma aplicação JEE, e começamos a ver profile daqui, examina relatórios dali, nisso um amigo de projeto, que a pouco tempo havia se enturmado com Rails, teve uma idéia: "Por que não fazemos cache das páginas mais frequentes, podemos criar um pequeno framework, e fazer como o Rails faz", explicando... em Rails basta colocar no controller as páginas que você deseja que sejam "cacheadas". Ex:

RUBY:
  1. caches_page :noticias

Pronto, a primeira chamada à action "noticias" cria o cache, e será usada pelas chamadas subsequentes. Para expirar o cache basta chamar:

RUBY:
  1. expire_page(:controller => 'public', :action => 'noticias')

Mais detalhes e excelente tutorial no Rails Envy.

Eu gosto muito de criar coisas novas, mas como dito no último post, criar mais um mini-framework para dar manutenção em uma aplicação já não muito pequena... bem, não obrigado. Foi por isso que eu disse: "Cara, dá uma pesquisada, com certeza alguém já fez isso." E pra variar, sim alguém já fez isso.

O excelente e bem conhecido EhCache possui esta funcionalidade e muitas outras.

Este mini tutorial parte do pressuposto que já se tem conhecimento do EhCache e de ServletFilter, para quem não conhece qualquer um dos dois, sem pânico, são muito fáceis de serem utilizados, vale uma pesquisada por tutoriais.

Para facilitar a vida, podem baixar este anexo com a aplicação-exemplo que fiz, basta colocá-lo em seu container web predileto, no meu caso testei no Tomcat, e acessar as URLs de teste:

http://localhost:8080/cache/semcache/teste.jsp

http://localhost:8080/cache/comcache/teste.jsp

O exemplo, mostra a hora atual em milesegundos. A página com cache, após acessada, terá 10 seg de vida.

Para implementar bastou os seguintes passos:

- Baixar e instalar os jars EhCache (ehcache-1.3.0-beta.jar), EhCache Constructors (ehcache-constructs-0.7.3.jar) e Commons-logging (dependência requerida) no diretório lib.

- Incluir no arquivo ehcache.xml as configurações para cache de página, as propriedades falam por si, configurem a gosto:

XML:
  1. <cache name="SimplePageCachingFilter"
  2.     maxElementsInMemory="10000"
  3.     maxElementsOnDisk="1000"
  4.     eternal="false"
  5.     overflowToDisk="true"
  6.     timeToIdleSeconds="10"
  7.     timeToLiveSeconds="10"
  8.     memoryStoreEvictionPolicy="LRU"
  9. >

- Configurar um ServletFilter no web.xml, para a implementação de filtro SimplePageCachingFilter

XML:
  1. <filter>
  2.    <filter-name>SimplePageCachingFilter</filter-name>
  3.    <filter-class>net.sf.ehcache.constructs.web.filter.SimplePageCachingFilter</filter-class>
  4.    <init-param>
  5.        <param-name>suppressStackTraces</param-name>
  6.        <param-value>false</param-value>
  7.        <description>Whether to suppress stack traces in the filter</description>
  8.    </init-param>
  9. </filter>

- Indicar para quais páginas fazer cache a partir do ServletFilter definido acima, dentro do web.xml. No exemplo, tudo que estiver abaixo do diretório "comcache".

XML:
  1. <filter-mapping>
  2.    <filter-name>SimplePageCachingFilter</filter-name>
  3.    <url-pattern>/comcache/*</url-pattern>
  4. </filter-mapping>

Pronto, é somente isso, a página será devidamente cacheada, seguindo as configurações do ehcache.xml.

Caso queira fazer cache de apenas uma parte da página, no caso de quem utiliza jsp:include, basta utilizar a outra implementação PageFragmentCachingFilter

Essa pequena implementação já garantiu um bom desempenho em várias páginas e sem reinventar a roda criando um mini-framework para esta função.

Se alguém souber de outro meio para fazer cache de página em Java, por favor esteja a vontade para comentar, gostaria muito de conhecer outras implementações.

Comments

Getting Real, o princípio K.I.S.S. aplicado em metodologia

Faz pouco mais de 4 meses que comecei a utilizar Rails em alguns projetos, mas Java ainda é minha linguagem do coração, e algo que me surpreendeu muito foi a filosofia por trás do Rails, ele não é um framework matador de horda de dragões, e não é pra ser mesmo, seu próprio criador David Heinemeier Hansson não tem a intenção de adicionar um milhão de funcionalidades, e tem motivo pra isso, trabalha na 37signals, prercursora da metodologia Getting Real, uma verdadeira aula de como evitar a burocracia e extrair muita produtividade, considero o livro radical em alguns pontos, mas isso é questão de opinião, e é claro adequação à realidade do seu projeto.

E aqui cabe a pergunta, você não está fazendo coisas demais no seu projeto ?

Os frameworks hoje em dia parecem querer abraçar o mundo, querem estar em todos os tipos de projetos, seus criadores prometem funcionalidades novas e/ou extravagantes a cada dia, e nessa vontade de fazê-lo crescer, vai tornando-o apenas cada vez mais "gordo", de difícil manutenção, e com isso nenhum projeto é ajudado. Sem contar a ausência de testes em muitos deles. Não quero dizer que Rails é o melhor de todos, estou citando apenas a filosofia por trás dele, há outros frameworks ótimos no mercado, tudo depende de pensar no que essencialmente o projeto precisa e com isso encontrar o framework correto.

Uma resposta para a enorme "gordura" de alguns frameworks é sempre: "Seu projeto tende a crescer, você vai precisar dos demais recursos oferecidos". Sinceramente não gostaria de precisar usar tudo, não quero complexidade demais no projeto só para ganhar mais manutenção. Gosto da filosofia K.I.S.S. (Keep It Simple Stupid). O que o meu cliente vai ganhar com esta funcionalidade X ? Ela é realmente é necessária ? Quanto mais simples, menos manutenção, mais tempo você tem para funcionalidades novas e simples.

Vocês podem pensar que isso é preguiça mas não é, recentemente houve um caso interessante, eu precisava adicionar um editor de texto na aplicação, escolhi o excelente FckEditor, ele possui ótimas funcionalidades para um editor de texto em browser, a primeira coisa que fiz foi reduzir a quantidade de botões. Apresentei ao cliente, ele usou um pouco e disse "acho que o pessoal vai estranhar um pouco a princípio, tem como melhorar o layout ?", mas o problema não era layout, observando como ele usou a ferramenta reduzi novamente a quantidade de botões para ter apenas o que ele realmente precisava e apresentei novamente. O resultado: "Puxa o layout melhorou mesmo, mais intuitivo", ele nem reparou que a quantidade de botões foi diminuída, eu apenas apresentei o que ele precisava, e nem uma linha a mais. Se o seu cliente precisa de uma funcionalidade supérflua, que ele pediu por impulso, convença-o do contrário, ou ambos sofrerão com uma funcionalidade que talvez nem seja usada, está lá apenas para formentar a entropia.

É como o novato que lê um livro sobre padrões de projeto e acha que deve usar todos de uma vez, é necessário pensar no básico. Keep It Simple!

Comments (1)

Nem Java suporta métodos gordinhos

Fato interessante ocorreu com uma amiga que estava trabalhando em um JSP enorme, bem que poderia haver menos dados a serem mostrados, mas não sou eu quem decido... enfim... lá pelas tantas, o compilador começou a reclamar de "code too large", caramba como assim ? Era inserir mais um caracter e dava erro, tirava o caracter e funcionava...

Bom não tem jeito, Google ao resgate... minutos depois problema encontrado e resolvido, o que ocorre é um limitação imposta pelo próprio compilador, um método em Java pode ter um tamanho máximo de 65534 bytes, portanto quando o JSP era convertido em Servlet acabava gerando esse método monstrinho gordinho, mais informações sobre este e outros limites você encontra aqui na especificação da Virtual Machine.

Para resolver o problema foi necessário, adivinhem ? Simmmm, REFATORAR..... basta dividir o JSP em arquivos menores que façam mais sentido e uni-los com <jsp:include>. Para ajudar ainda mais não se esqueça de utilizar padrões web para o HTML gerado, mais tableless, o código fica muito mais enxuto, e quando possível, pense bem antes de criar uma tela com tantos dados, para uma melhor usabilidade.

Claro que, embora seja uma limitação imposta pelo compilador, NUNCA crie um método tão grande, isso vale para qualquer linguagem.

Comments

Classloader: como utilizar

É incrível como aprendemos algo novo todo dia, mesma que a lição venha de forma lenta e dolorosa... recentemente, em uma empresa onde presto serviços em Java, tivemos um problema com o DWR (tenho enorme paixão por este componente, o modo como fizeram a ponte entre Java e Javascript chega a ser mágico, é o estado da arte em uso de Javascript, mas isso fica pra outro 'post'... =D)

Pois bem, o problema foi ocasionado durante testes de migração pra o JBoss 4.x, a inicialização do DWR estava falhando, não conseguia de forma alguma encontrar as classes informadas em seu arquivo de configuração xml, o famigerado NoClassDefFoundError, algo que não apresentava problemas no JBoss 3.x. Após olhar em empacotamento, meta-infs, jars, configurações do JBoss, nada, nenhuma pista, nem o Google ajudou. O jeito foi descer mais fundo, ler o código fonte do DWR. A linha onde o erro ocorre, executa apenas um carregamento de classe:

this.classDefinition = Class.forName(className) ;

A classe é guardada para que possa ser instanciada por reflexão posteriormente, o interessante é que tudo isso ocorre na inicialização do JBoss, depois que a aplicação levanta, tentamos via debugger, executar o mesmo comando e voilá... funcionava perfeitamente... o que raios estava acontecendo ? Não sabíamos....

Bom, não teve jeito, a classe do DWR teve de ser alterada para testar outra possibilidade de carregamento. Com isso trocamos a linha para:

this.classDefinition = Thread.currentThread().getContextClassLoader().loadClass(className);

Linha monstruosa.... mas funcionou!

Boa prática aprendida: o Class.forName() não sobe na hierarquia de ClassLoader, fica apenas no ClassLoader local, enquanto o ClassLoader.loadClass() sim, ou seja, por questão de robustez e boa prática, sempre utilize a segunda forma, para não ficar com cara de "ué", quando este tipo de problema surgir.

Mas por que no JBoss 3.x funcionava e no 4.x não ? A resposta é que o ClassLoader o 4.x foi reescrito para ficar mais aderente as especificações, com isso o Class.forName() pegou todo mundo desprevinido, houve claras reclamações da comunidade, mas temos que viver com isso..., existem práticas a serem seguidas.

Ainda bem que não tivemos que usar o código alterado do DWR, estávamos na versão 1.1.3, por curiosidade, baixei a versão estável mais recente (1.1.4) e pra minha grata surpresa essa linha já havia sido alterada do mesmo modo que fizemos :D , só fiquei chateado por essa alteração não estar presente no release notes, teria me economizado tempo durante busca por pistas...

Comments