JBoss + Hibernate + MySQL sem perder ligações

28 \28UTC Novembro, 2008

Existem essencialmente duas formas distintas de usar Hibernate com MySQL (ou qualquer outra base-de-dados, para este ponto em concreto é indiferente), definindo todas as propriedades da ligação no próprio ficheiro de configuração do Hibernate, ou dizendo apenas que se pretende usar uma datasource JNDI pré-existente.

A primeira opção é a escolha típica numa aplicação stand-alone, mas a opção de utilizar uma datasource JNDI é a que faz mais sentido para application servers. Portanto, o ficheiro de configuração do Hibernate há-de ter, parcialmente, este aspecto:

<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.datasource">
java:my_app_name
</property>
<property name="hibernate.setup">true</property>
<property name="hibernate.dialect">
org.hibernate.dialect.MySQLInnoDBDialect
</property>
<property name="transaction.factory_class">
org.hibernate.transaction.JDBCTransactionFactory
</property>
<property name="current_session_context_class">
thread
</property>
</session-factory>
</hibernate-configuration>

Depois basta definir a datasource com o nome JNDI correspondente, por exemplo assim:

<datasources>
<local-tx-datasource>
<jndi-name>my_app_name</jndi-name>
<connection-url>jdbc:mysql://host_or_ip/db_name</connection-url>
<driver-class>com.mysql.jdbc.Driver</driver-class>
<user-name>username</user-name>
<password>password</password>
<min-pool-size>5</min-pool-size>
<max-pool-size>20</max-pool-size>
</local-tx-datasource>
</datasources>

Agora que está tudo montado como manda a documentação, sugiro uma experiência:

  1. Ligar servidor BD.
  2. Ligar application server.
  3. Testar aplicação que se liga à base de dados (funciona)
  4. Reiniciar servidor BD (ou esperar 8 horas).
  5. Testar aplicação que se liga à base de dados (não funciona!)

Então… tanto trabalho para abstrair a ligação a base de dados e um simples restart da base de dados deixa as ligações geridas pelo Hibernate todas inválidas? E, pior, não há recuperação automática?

Pois… parece que não. E acrescentar o parâmetro autoReconnect=true ao URL JDBC também não resolve, aliás, a utilização desse parâmetro até é desaconselhada, como se pode ver pelo que diz no manual:

Should the driver try to re-establish stale and/or dead connections? If enabled the driver will throw an exception for a queries issued on a stale or dead connection, which belong to the current transaction, but will attempt reconnect before the next query issued on the connection in a new transaction. The use of this feature is not recommended, because it has side effects related to session state and data consistency when applications don’t handle SQLExceptions properly, and is only designed to be used when you are unable to configure your application to handle SQLExceptions resulting from dead and stale connections properly. — MySQL 5.0 Reference Manual

A solução que encontrei para MySQL (e que também funciona para MS SQL Server), é acrescentar a seguinte linha ao ficheiro de configuração da datasource:

<check-valid-connection-sql>SELECT 1</check-valid-connection-sql>

Esta opção faz mesmo aquilo que parece fazer, ou seja, antes de usar uma ligação, a query ali especificada é executada como forma de verificar se a ligação é usável. Se não ocorrer nenhum erro, a instrução SQL que realmente se pretende executar é enviada pela mesma ligação, se houver um erro, a ligação é descartada e é criada uma nova.

Claro que isto tem um impacto sério na performance uma vez que se está a duplicar o número de queries, o que pode ser particularmente grave se o acesso à base de dados for feito através de um canal de elevada latência (rede, por exemplo).

Aparentemente existem soluções melhores para outras bases de dados, como o seguinte comentário parece sugerir:

 

Whilst you can use the old “select 1 from dual” trick, the downside with this is that it issues an extra query each and every time you borrow a connection from the pool. For high volumes, this is wasteful.

JBoss provides a special connection validator which should be used for Oracle:

org.jboss.resource.adapter.jdbc.vendor.OracleValidConnectionChecker

This makes use of the proprietary ping() method on the Oracle JDBC Connection class, and uses the driver’s underlying networking code to determine if the connection is still alive. — stackoverflow.com

Alguém conhece uma alternativa semelhante para MySQL?


Formulário de Login

13 \13UTC Novembro, 2007

Quantas aplicações web têm formulários de login? Milhares? Milhões?
Destas, quantas é que solicitam como credenciais um nome de utilizador e uma palavra-chave? 90%? 99%? 99,999%?

Sou eu que estou a pensar mal, ou não seria de esperar que por esta altura um formulário de login fosse uma comodity, com meia dúzia de sabores possíveis, todos eles conhecidos de toda a gente e tão fáceis de usar como um mamilo?

Então porque é que ainda se vêm formulários de login como este?

Site www.abola.pt

O que é que está o link de Ajuda a fazer ali no meio dos dois campos? Para quê os bullets vermelhos do tamanho das letras?

E não, não é um problema de incompatibilidade de browser… tem o mesmo aspecto em Firefox e IE7.


SSL Trust

7 \07UTC Novembro, 2007

Apenas uma nota muito rápida sobre um tema (SSL) que já me consumiu umas horas valentes.

Se uma aplicação escrita em Java necessitar de aceder a um URL usando HTTPS como transporte, o security manager tenta validar o certificado apresentado pelo servidor remoto.

Em ambiente de desenvolvimento, e mesmo em alguns ambientes de produção muito particulares, é usual que os certificados sejam auto-assinados, e por isso inválidos do ponto de vista do security manager. A solução usual, e conhecida de toda a gente, é adicionar o certificado auto-assinado à keystore do JRE que lança o processo Java. Isto resolve o problema.

No entanto, também acontece em ambiente de desenvolvimento (e em alguns ambientes de produção, já não tão “particulares” como no caso anterior) o certificado ser inválido por outro motivo: o CommonName não corresponder ao URL usado para aceder ao servidor. Neste caso, adicionar o certificado à keystore não adianta nada, o security manager vai continuar a impedir a ligação.

Uma forma de resolver o problema está descrita neste wikibook. Consiste essencialmente em acrescentar uma classe à aplicação em causa e chamar o seguinte método antes de tentar aceder ao URL:

SSLUtilities.trustAllHostnames()

Edit: A dica vem da Teresa Frias, obrigado!


Novos portáteis Lenovo

10 \10UTC Julho, 2007

Recebi recentemente um portátil novo: um Thinkpad R61. Se dependesse só de mim esperava que o T61 chegasse ao distribuidor, mas não se pode ter tudo…

Já vem com o Windows Vista Business instalado e decidi manter, por um lado porque é que o caminho da menor resistência, e por outro porque gosto de experimentar coisas novas, venham elas da Microsoft ou não.

Foi a primeira vez que usei Vista durante mais de 5 minutos e a impressão foi bastante boa… até abrir o Internet Explorer! Já usava IE7 há bastante tempo no Windows XP e nunca tinha visto um comportamento tão mau como este. O browser abria bem rápido, mas depois engasgava durante uns 5 segundos antes de apresentar a página inicial – quer por acaso era about:blank. A mesma história repetia-se quando tentava abrir uma tab nova. Abrir uma tab é uma acção que espero que seja instantânea, esperar 5 segundos por isso é completamente inaceitável!

Mas é nestas alturas que dá jeito não ser fundamentalista e pensar dois minutos antes de culpar o Vista e instalar outro sistema operativo. Depois de procurar um bocado descobri que, basicamente, a culpa é de uma extensão para o IE7 que a Lenovo inclui nas instalações mais recentes. O add-on é o Lenovo Password Manager, que faz parte da ThinkVantage Client Security Solution. A extensão mantém-se activa mesmo que se desactive esta opção no Client Security Center, o que obriga a desactivar o add-on no IE para resolver o problema.

De uma forma geral acabei por desactivar a maior parte do software Lenovo / Thinkvantage que vem instalado de base. Mantenho o on-screen display, o power manager e pouco mais…


JConsole

2 \02UTC Junho, 2007

O JConsole é um utilitário (infelizmente apenas disponível para Windows) que vem incluído no JDK da Sun a partir da versão 1.5.0 e que permite monitorizar uma JVM local ou remota através de JMX.

A ferramenta é muito útil para monitorizar application servers como o Tomcat ou o JBoss e já me permitiu identificar situações de memory leak e deadlock que de outra forma teriam passado indetectadas.

No caso em que se pretende monitorizar JVMs remotas, estas devem ter sido inicializadas com alguns parâmetros especiais:

-Dcom.sun.management.jmxremote.port=9999
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false

As opções são auto-explicativas. Pode ligar-se a autenticação e o SSL em ambientes mais agressivos, mas para monitorização em “tempo real” mantê-las desligadas acelera significativamente o refrescamento.

A comunicação entre o utilitário e a JVM é feita por RMI. Isto não teria particular relevância, não fosse o facto de o RMI se portar mal através de NAT’s, mais concretamente através de DNAT’s. Esta semana fui “mordido” por esta característica e se não fosse a FAQ sobre RMI e estes dois artigos, não me safava… A solução é simples, é preciso dar um hint à JVM sobre qual o hostname ou endereço ip que vai ser usado para lhe aceder:

-Djava.rmi.server.hostname=[fqdn or dnated ip]

Nota: o utilitário apenas existe para Windows, mas podem ser monitorizadas JVMs a correr em qualquer plataforma…


Seguir

Get every new post delivered to your Inbox.