O Bug do Firebug

Aproveito a oportunidade para agradecer publicamente ao Joe Hewitt pelo seu excelente Firebug que já me poupou muitas horas de debugging.

Para quem não conhece, o Firebug é um add-on que integra com o Firefox fornecendo uma grande diversidade de ferramentas de desenvolvimento web. Com o Firebug podemos, entre outras coisas:

  • inspeccionar e editar HTML;
  • inspeccionar, editar e obter métricas de CSS;
  • inspeccionar e editar o DOM;
  • monitorizar a actividade de rede, especialmente útil para controlar os pedidos Ajax e para medição de tempos de resposta;
  • fazer debugging de javascript, com breakpoints, step-by-step debugging, etc.
  • aceder à consola interactiva, permitindo fazer logging, visualizar mensagens de erro do browser e executar directamente javascript.

E como se isto não bastasse, saiu recentemente um versão lite que permite executar o Firebug noutros browsers : Internet Explorer, Opera, Safari.

Voltando ao tema, estava a fazer uns testes com Ajax que envolviam ir buscar uma página a outro domínio, processar a resposta para extrair parte da informação e apresentar o conteúdo processado na própria pagina.

O problema é que cada vez que tentava contactar o site remoto, o Firefox queixava-se com o seguinte erro:
uncaught exception: Permission denied to call method XMLHttpRequest.open

Em menos de 5 minutos o Google explicou-me ;-) que o problema se devia a uma restrição de segurança implementada no Firefox, que para fazer este tipo de operação o script tinha de ter privilégios de “UniversalBrowserRead” e que isso se conseguia executando algumas linhas de javascript antes de estabelecer a ligação remota.

E foi o que fiz… Eis a versão final do ficheiro:



    
    

    

    
    

Quando voltei a testar, realmente o Firefox mostrou um alerta a perguntar se queria dar a tal permissão ao script.

É claro que aceitei, na expectativa de finalmente ver a coisa a funcionar.

Estava enganado, o mesmo erro voltava a aparecer na consola do Firebug.

Na altura, como o que eu estava a testar nem sequer estava directamente relacionado com Ajax, acabei por desistir: mudei o ficheiro .html para .jsp, e fiz uma servlet para ir buscar o conteúdo que precisava (com HttpURLConnection).

O problema ficou assim rapidamente resolvido mas por outro lado esta solução obrigava-me a ter de executar o Tomcat que de outra forma não era preciso.

Ontem, ao fazer restart do Firefox, reparei que o Firebug foi actualizado para a versão 1.0.5 e aproveitei para ir dar uma vista de olhos nas release notes.

Estão desactualizadas e ficaram na versão 1.0.1 :-(

Já que lá estava, detive-me um bocado a ler o blog e as faqs e eis que encontro isto:


Why don't XMLHttpRequests work after using permissions manager to enable privileges?


Unfortunately, there is a

bug in Firebug that prevents enablePrivilege from allowing XMLHttpRequests to external domains.
(o sublinhado é meu)


To work around this problem, select "Disable Network Monitoring" from the Options menu in Firebug's Net tab.

Fui buscar a versão antiga do código que tinha feito, segui as instruções, ou seja desliguei o 'Network Monitoring' nas Opções na tab Net e tudo funcionou na perfeição!

Note to Self : Existem outros browsers para além do Firefox e deve-se ler sempre a documentação das APIs ou ferramentas antes de as usarmos.

zp8497586rq

Camadas em Camadas

Layers vs. Tiers é um daqueles assuntos que para os developers tem um cariz religioso. Basta falar no assunto para de imediato surgirem opiniões inflamadas sobre o que é e o que não é cada uma delas.

Deixo aqui a minha sobre este assunto…

A verdade é que a própria tradução das palavras para português não ajuda : se Layer se traduz directamente para 'camada' o certo é que a melhor tradução para Tier é mesmo… camada.

A diferença de conceitos, mesmo em inglês é bastante subtil: uma Tier é cada uma das Layers quando estão dispostas em camadas! (cf. por exemplo, tier on WordNet).

Para complicar a situação, levando em conta esta definição de que uma Tier é cada uma das Layers quando se encontram dispostas em camadas (ou ligadas, ou 'enfileiradas'), depois de estabelecidas as relações entre as várias Layers estas passam frequentemente a ser identificadas como Tiers na documentação técnica.

Temos portanto camadas e camadas.

Em informática, contudo, estas palavras têm significados bastante diferentes, uma vez que se referem a coisas distintas: quando falamos de Layers referimos-nos a Camadas Lógicas e quando falarmos em Tiers referimos-nos a Camadas Físicas.

Camadas Lógicas (Layers)

As camadas lógicas referem-se à organização interna de cada um dos elementos de software representando uma divisão das suas responsabilidades.
Cada camadas colabora apenas com as camadas que lhe são adjacentes através da troca de mensagens. Esta colaboração recorre frequentemente à utilização de padrões de desenho (design patterns).

Esta forma de organização facilita a manutenção do código uma vez que ao isolar as competências, reduz as dependências entre os elementos e permite que alterações feitas a uma das camadas tenha pouco ou nenhum impacto nas camadas adjacentes. Facilita também o desenvolvimento em equipa uma vez que permite que código de várias camadas seja desenvolvido em paralelo e até mesmo com recurso a diferentes tecnologias.

Tomemos como exemplo uma aplicação web cujo objectivo, de uma forma muito simplista, é estabelecer a ligação entre o utilizador e uma base de dados, processando a informação trocada entre os dois.

De certa forma, isto define de imediato três camadas naturais :

  • a camada de apresentação de dados (presentation layer), responsável pela apresentação e recolha de dados entre o utilizador e o sistema;
  • a camada de negócio (business layer) responsável pelo processamento da informação trocada entre o utilizador e a base de dados;
  • e a camada de acesso aos dados (data access layer) responsável pela persistência e consulta da informação associada ao sistema na base de dados.


Outras camadas (lógicas) poderão ser identificadas no contexto de um sistema, c

omo por exemplo a camada de segurança ou a camada de acesso a sistemas externos.

Camadas Físicas (Tiers)

As camadas físicas referem-se à distribuição de cada um dos elementos de software em tempo de execução (runtime).

Esta distribuição tanto se pode referir a hardware distinto como a outros elementos de software.

Tal como nas camadas lógicas, cada camadas física colabora apenas com as camadas que lhe são adjacentes recorrendo a protocolos de comunicação bem definidos.

Esta divisão facilita a administração do sistema e aumenta o seu potencial de crescimento (escalabilidade).

Numa aplicação web, podemos distribuir software por pelo menos três camadas físicas :

  • a camada cliente (client tier) que poderá ser o próprio browser do utilizador (caso das applets), um web server a distribuir conteúdos estáticos (ex: HTML) ou um application server a distribuir conteúdos dinâmicos (ex: JSPs, XML);
  • a camada de negócio (business tier) que poderá ser um application server a gerir EJBs ou qualquer outro tipo de middleware;
  • a camada de dados, (data tier) normalmente composta por um sistema de gestão de base de dados (DBMS).

Estas camadas poderão estar (e normalmente estão) distribuídas em máquinas separadas.

Enquanto Developers, ao desenvolver este tipo de sistemas, frequentemente executamos a aplicação completa na nossa máquina (execução local). Muitas vezes nem nos apercebemos disto mas temos (a lista não é exaustiva):

  • o hardware da nossa máquina (por definição sempre insuficiente ;-) );
  • um sistema operativo (ou mesmo o Windows :-) );
  • pelo menos uma JVM;
  • um webserver ou um application server ou uma mistura dos dois;
  • um browser;
  • eventualmente, uma base de dados;

Cada um destes componentes comunica apenas com as adjacentes – o sistema operativo tem de conhecer o hardware, a JVM tem de conhecer o sistema operativo mas não o hardware, o webserver precisa de conhecer a JVM mas não o sistema operativo, e assim por diante.

A disposição destes componentes leva-nos de imediato a pensar em camadas e o facto de estarmos a falar de componentes de software, todos a serem executados num só elemento físico remete-nos para as layers. Errado!

Pelo facto de estarmos a falar do relacionamento externo entre

os componentes de software em tempo de execução e não na sua organização interna, devemos considerá-las como tiers. Cada uma destas tier deverá estar internamente organizada em layers (bem, em relação ao Windows… :-) )

Concluindo, na minha opinião, Layers são Camadas Lógicas e Tiers são Camadas Físicas. Não existe uma relação predefinida entre Camadas Lógicas e Camadas Físicas num sistema. Uma Camada Física pode hospedar diversas Camadas Lógicas e uma Camada Lógica pode ocupar diversas Camadas Físicas.

zp8497586rq

Primitivas (mal) embrulhadas

Com a introdução do autoboxing na versão 1.5 do Java, a utilização de primitivas começou a cair em desuso, ao ponto de até já haver quem se pergunte se se deve continuar a referir este tipo de variável no ensino do Java (http://www.evolutionnext.com/blog/2004/07/03/1088886468000.html).

Sem duvida que o autoboxing trouxe muitas vantagens a quem escreve código, entre elas, permitir a utilização de primitivas como chaves ou valores nas Collections, mas por outro lado também permite fazer coisas menos bonitas como usar Integers como índices nos ciclos for.

Talvez devido ao facto da sua introdução na linguagem ser relativamente recente, os Wrappers correspondentes a cada uma dos tipos primitivos (Integer, Boolean, Double, etc) nem sempre são usados da forma mais correcta, como é caso dos dois exemplos seguintes que tenho encontrado de forma recorrente em código que revejo:

1 – utilizar == para comparar referências de Wrappers

Depois do compilador converter a primitiva para o Wrapper correspondente, esta passa a ser um objecto e como tal, sujeito às mesmas regras, ou seja, a comparação da igualdade deve ser feita invocando o método .equals() numa referencia válida de um dos Wrappers e não usando o operador ==.

Passemos então ao exemplo:

public class Equalities {
    public static void main(String[] args) {
        Integer i = 150;
         Integer j = 150;
        System.out.println(i==j); // false
        System.out.println(i.equals(j)); //true
    }
}

A comparação de valores usando o operador == falha enquanto que a utilização do método .equals() devolve o resultado esperado. É realmente discutível se o operador == deveria ou não ser 'overloaded' para os Wrappers, como já acontece para as String, mas i

ersion Survey Site: Converts 1 In 7'>Best Conversion Survey Site: Converts 1 In 7

sso fica para outro post ;-)

Já agora uma curiosidade: pela forma como a classe Integer está implementada, se usarmos nas variáveis i e j valores entre -128 e 127, ambos os métodos se comportam da forma esperada, ou seja o operador == devolve true!

2 – Não verificar a validade das referências dos Wrappers quando são passadas como parâmetros para métodos que declaram tipos primitivos

(não liguem, eu gosto de complicar :-) ).

mais uma vez, isto explica-se melhor com um exemplo:

public class Wrapped 
    public static void main(String[] args) {
        Integer testValue=null;
	
		
            new Object(){
            public void m1(Integer p){ // <-- o tipo do parâmetro é um wrapper
                System.out.println(p);
            };
        }.m1(testValue); //escreve 'null'
		
		
        new Object(){
            public void m2(int p){ // <-- o tipo do parâmetro é uma primitiva
                System.out.println(p);
            };
        }.m2(testValue); //lança uma NullPointerException!
    }
}

Neste caso, enquanto a chamada ao método m1 escreve, como esperado, 'null', a invocação do método m2 trás-nos a desagradável surpresa: uma NullPoiterException.

O que se passa é que antes de invocar o método m2, a JVM executa o correspondente a invocar o método testValue.intValue() na referencia testValue que por coincidência (ou não, neste caso) é null...

Já agora (mais) uma curiosidade: o FindBugs? detecta correctamente o problema no método m2 (que passa incólume pelo compilador) e assinala-o como 'Null pointer dereference'.

Conclusão : embrulhos sim senhor, mas não se metam em embrulhadas ;-)

zp8497586rq
zp8497586rq