Tenho por hábito usar o conjunto de teclas Ctrl+Tab
para alternar entre tabs em várias aplicações que uso (Firefox, NetBeans, etc.)
No Eclipse, por omissão, esta funcionalidade está configurada para as teclas Ctrl+F6cheapest cialis
Tenho por hábito usar o conjunto de teclas Ctrl+Tab
para alternar entre tabs em várias aplicações que uso (Firefox, NetBeans, etc.)
No Eclipse, por omissão, esta funcionalidade está configurada para as teclas Ctrl+F6cheapest cialis
ode>.
Para alterarem a configuração deste atalho no Eclipse (Europa) eis o que devem fazer :
Window > Preferences
Seleccionem na árvore General > Keys
Next Editor
'Ctrl+Tab
).Ctrl+F6
original, caso pretendam)É claro que poderão usar este mesmo método para mudar outros atalhos do Eclipse.
Tal como prometido no meu último artigo, aqui ficam as instruções para configurar o Liferay Portal para aceder a uma base de dados MySQL usando o ambiente de desenvolvimento com o eclipse.
>1 – Criação da Base de Dados MySQL
Criem a base de dados com o nome lportal.
Poderão fazer isto utilizando o MySQL Administrator (a ferramenta de administração do MySQL) ou usando a seguinte linha de comando:
mysqladmin --default-character-set=utf8 create lportal
Abram o Data Source Explorer do eclipse (Window -> Show view -> Data Source Explorer
).
Seleccionem o icon ‘New Connection Profile’ ou usem o botão direito do rato do sobre ‘Databases’ e depois seleccionem ‘New’.
Na janela ‘New Connection Profile’ seleccionem o tipo SQL Model-JDBC Connection.
No quadro seguinte indiquem um nome para o perfil (ex: ‘Liferay – Mysql’).
No último quadro, usem o botão de procura (‘…’) na lista “Select a browser”.
nova definição:
Database -> MySQL -> 5.0 -> MySQL JDBC Driver
).$WORKSPACE/ext/lib/development/mysql.jar
($WORKSPACE
corresponde à localização da pasta do vosso workspace no sistema de ficheiros);lportal
‘.Database -> MySQL -> 5.0 -> Liferay MySQL Driver
).Voltem de novo à janela de criação do perfil e validem que todas as informações estão de acordo com a vossa configuração (podem testar a ligação a partir desta janela).
Editem o ficheiro create-mysql.sql
na pasta /sql/create
do projecto ‘ext’.
Caso não tenham memoria para desperdiçar (é o meu caso) respondam afirmativamente quando o eclipse perguntar se pretendem desligar a validação de sintaxe do ficheiro.
No topo da janela de edição do ficheiro encontram a configuração do ‘Connection profile’. Seleccionem o tipo Generic JDBC_1.x
, o driver ‘Liferay - Mysql
‘ e a base de dados lportal
.
Nota: caso o status não seja ‘Connected
‘ não vão conseguir ver o nome da base de dados.
Neste caso devem aceder ao Data Source Explorer ( Window -> Show View -> Data Source Explorer
), seleccionar a Database ‘Liferay – MySQL’ e fazer connect (botão direito do rato)
Comentem as três primeiras linhas do ficheiro, uma vez que a base de dados já foi criada
-- drop database if exists lportal;
-- create database lportal character set utf8;
-- use lportal;
Gravem e executem o sql (ctrl+alt+X ou Execute All no menu contextual).
Editem o ficheiro Root.xml
localizado na pasta servers/tomcat/conf/Catalina/localhost
do projecto ‘ext’.
Comentem o datasource do Hypersonic e descomentem o datasource do MySQL.
Preencham os atributos username
e password
e, caso não estejam a usar os valores default do MySQL, editem tambem o valor do atributo url
, de acordo com a vossa configuração.
Gravem as alterações.
Copiem o ficheiro Root.xml
para a pasta conf/Catalina/localhost
do tomcat.
Copiem e o ficheiro mysql.jar
para para a pasta lib/ext
do tomcat.
Voltarei a falar sobre a forma correcta de fazer deploy destes ficheiros no meu próximo artigo.
O Liferay Portal é uma solução empresarial de portal, open-source, desenvolvida em Java. Precisei recentemente de instalar o ambiente de desenvolvimento. A documentação disponível refere-se à v
ersão 4.2, está desactualizada e por isso contem bastantes incorrecções. Como o processo não é lá muito intuitivo resolvi fazer uma lista dos passos necessários para concluir a instalação. Aqui fica:
$LIFERAY_HOME
. Dêem-lhe um nome sugestivo como por exemplo ‘liferay-portal’.$LIFERAY_HOME/tomcat
e $LIFERAY_HOME/workspace
).$LIFERAY_HOME
/tomcat
.$LIFERAY_HOME
/tomcat/bin/startup.bat
(ou startup.sh
).http://localhost:8080/
. Se tudo estiver bem devem conseguir ver a página inicial do liferay-portal.user
]: test@liferay.com
[pass
]: test
$LIFERAY_HOME/workspace
– $WORKSPACE
File
> Switch Workspace
>
Other
(indiquem a pasta $WORKSPACE
e confirmem).
File
> Import
> SVN
> Projects from SVN
.
https://lportal.svn.sourceforge.net/svnroot/lportal
.portal
‘ e dentro deste o ramo ‘trunk
‘.Check out as a project with the name specified:
‘ e indiquem como nome do projecto ‘liferay-portal
‘.Checkout recursively
‘ seleccionada.portal
‘ no Project Explorer do eclipse. Nota: Se tiverem problemas no checkout voltem a repetir os passos anteriores mas desta vez desseleccionem a recursividade (Checkout recursively
). O checkout termina rapidamente. Depois usem a opção team syncronize/update para obterem o projecto completo. Isto permite ter mais controlo sobre a operação pois, neste caso, é possível interromper o processo a qualquer altura e retomar posteriormente desde o ponto onde ficou.
Antes de dar inicio a este passo devem verificar se têm o ant 1.7.0
instalado. O build não funciona com versões anteriores. O Liferay usa o Jikes
como compilador default. Se o pretendem usar e ainda não o têm instalado, agora é a altura ideal para o fazerem ;-). No meu caso, a natural preguiça levou-me a optar pelo habitual javac
.
$WORKSPACE/portal
.build.$USER.properties
, onde $USER
deve ser substituido pelo vosso username na máquina ou, alternativamente, o nome da própria máquina, com o seguinte conteúdo: lp.ext.dir=$WORKSPACE/ext lp.eclipse.project.name=liferay-ext javac.fork=true javac.memoryMaximumSize=512m javac.compiler=modern
(ignorem esta se estiverem a usar o jikes
e não se esqueçam de substituir $WORKSPACE
pelo path correspondente).ant clean start build-ext
ext
no vosso workspace.$WORKSPACE/ext
) criem o ficheiro app.server.$USER.properties
onde, mais uma vez, $USER
deve ser substituido pelo vosso username na máquina ou, alternativamente, o nome da própria máquina, e com o seguinte conteúdo : app.server.parent.dir= $LIFERAY_HOME app.server.tomcat.dir=${app.server.parent.dir}/tomcat
(se estiverem a instalar um bundle diferente devem consultar os nomes das properties no ficheiro app.server.properties
).jikes
como compilador devem ainda criar o ficheiro build.$USER.properties
, com o conteúdo: javac.compiler=modern javac.fork=true javac.memoryMaximumSize=256m
$WORKSPACE/ext
), executem o comando: ant deploy
.File
> Import
> Other
> Existing Folder As New Project
). Seleccionem a pasta $LIDERAY_HOME/ext
.portal
e ext
.ext
e acedam às suas properties
.Ant Builder
.build.xml
que se encontra na pasta raiz do projecto ext
($WORKSPACE/ext
).Java Builder
‘. O eclipse pede uma confirmação: aceitem!ext
.Create, manage and run configurations
‘ seleccionem a opção ‘Run on Server
‘ e depois o icon ‘new
‘.tomcat
‘ (ou outra, dependendo do bundle que estão a instalar).-Djava.endorsed.dirs=${workspace_loc:ext/servers/tomcat/common/endorsed} -Dcatalina.base=${workspace_loc:ext/servers/tomcat} -Dcatalina.home=${workspace_loc:ext/servers/tomcat} -Djava.io.tmpdir=${workspace_loc:ext/servers/tomcat/temp}
por -Djava.endorsed.dirs=${workspace_loc}/../tomcat/common/endorsed -Dcatalina.base=${workspace_loc}/../tomcat -Dcatalina.home=${workspace_loc}/../tomcat -Djava.io.tmpdir=${workspace_loc}/../tomcat/temp
$LIFERAY_HOME/tomcat/bin
).tools.jar
).bootstrap.jar
do tomcat/bin
como jar externo e removam a entrada existente para o mesmo ficheiro.Confirmem que tudo está a funcionar seguindo os passos indicados no ponto 2. É claro que não precisam de executar o tomcat, o eclipse já tratou disso. O próximo passo será configurar a base de dados, mas isso ficará para um outro post
Num blog em que o texto das várias entradas tende a ser extenso (como este), a página principal começa rapidamente a ter um comprimento considerável.
Quando se pretende consultar uma entrada mais antiga só temos duas hipóteses:
guimos o link permanente para a entrada (caixa ‘Posts Recentes’) e depois fazemos ‘back’ no browser;
Uma forma interessante de resolver este problema seria ter uma opção de toggle do texto das entradas, ou seja, ter uma opção para mostrar ou esconder o texto dos posts por escolha do utilizador.
Estava a matutar sobre isto enquanto admirava os tons campestres desta página e pensei que não era má ideia pôr a solução em prática.
Um requisito importante é que o teria de fazer sem alterar efectivamente a página (só para não estar a chatear o Administrador do blog durante o fim-de-semana ).
Outro é que o código teria de ser suficiente pequeno para poder ser postado aqui.
Era definitivamente um bom desafio para por à prova o jQuery.
Precisava de quatro operações para concretizar as alterações:
Usei o Firebug para analisar a estrutura do DOM da página.
Cada entrada tem uma estrutura fixa:
<h2>
com o link para o titulo;<h4>
com a data do post e a identificação do autor;<div>
com a class ‘entry’ com o texto;<p>
com a class ‘tagged’ com links para as tags e comentáriosPortanto, para concretizar a primeira operação só precisava de esconder os elementos com as classes ‘entry’ e ‘tagged’ que em jQuery se traduz como:
$('.entry, .tagged').hide();
Para a concretizar segunda operação acrescentei um link à frente do nome do autor, ou seja, de todos os <h4>
s precedidos por <h2>
s, ou em jQuery:
$('h2 ~ h4').append('<a href="javascript:void(0)" class="entryToggler">(Mostrar)</a>');
Para a terceira opção e visto que associei os links criados à class entryTogger, basta associar uma função ao evento onclick de todos os
elementos com esta classe ou em jQuery:
$('.entryToggler').click(toggleEntry);
Falta só desenvolver a função toggleEntry.
Uma vez que esta função tem acesso ao elemento anchor seleccionado pelo utilizador (this
), podia usá-lo como referência para:
var isHidden = $(this).text().search('Mostrar')!=-1;
var x=$(this).parent().next(); x = x.add(x.next());
isHidden ? x.slideDown('slow') : x.slideUp('slow');
isHidden ? $(this).text('(Esconder)') : $(this).text('(Mostrar)');
e estava feito!
Como podem testar o código? É fácil:
abram a página principal do blog. (pessoal do rss, tem mesmo que ser na página principal)
Se estiverem a usar o Firebug podem copiar o código completo para a consola e executarem-no:
var toggleEntry = function(){ var isHidden = $(this).text().search('Mostrar')!=-1; var x=$(this).parent().next(); x = x.add(x.next()); isHidden ? x.slideDown('slow') : x.slideUp('slow'); isHidden ? $(this).text('(Esconder)') : $(this).text('(Mostrar)'); }; $('.entry, .tagged').hide(); $('h2 ~ h4').append('<a href="javascript:void(0)" class="entryToggler">(Mostrar)</a>'); $('.entryToggler').click(toggleEntry);
Para os outros casos, escrevi o código todo numa só linha que podem copiar para a caixa de endereço do browser (substituindo o http://www.zonaj.org):
javascript:var toggleEntry = function(){var isHidden = $(this).text().search('Mostrar')!=-1;var x=$(this).parent().next();x = x.add(x.next());isHidden ? x.slideDown('slow') : x.slideUp('slow');isHidden ? $(this).text('(Esconder)') : $(this).text('(Mostrar)');};$('.entry, .tagged').hide();$('h2 ~ h4').append('<a href="javascript:void(0)" class="entryToggler">(Mostrar)</a>');$('.entryToggler').click(toggleEntry);void(0);
Testado com FF2, IE7 e Opera9.
É claro que tive a vantagem de não me ter de preocupar em importar o script de jQuery porque a própria página já o faz.
Caso estivesse a alterar o DOM de uma página que não use jQuery, podia fazer o mesmo tipo de alterações usando o jQuerify.
Nota 1:
Para deixar a primeira entrada sem alterações basta alterar a linha
$('.entry, .tagged').hide();
para
$('.entry, .tagged').not(':first').hide();
e a linha
$('h2 ~ h4').append('<a href="javascript:void(0)" class="entryToggler">(Mostrar)</a>');
para
$('h2 ~ h4').not(':first').append('<a href="javascript:void(0)" class="entryToggler">(Mostrar)</a>');
Nota 2:
Para remover a irritante ‘caixa picotada’ à volta do link quando este fica activo, acrescentar como ultima linha da função:
$(this).blur();
Nota 3:
O código final tem cerca de 10 (!) linhas e repeti-o 3 vezes neste post.
Nota 4:
Para ter de volta a página original basta fazer reload no browser (mesmo tendo alterado a linha de endereço).
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:
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.
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.
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 :
omo por exemplo a camada de segurança ou a camada de acesso a sistemas externos.
Outras camadas (lógicas) poderão ser identificadas no contexto de um sistema, c
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 :
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):
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.
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 Integer
s 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:
==
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
!
(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
Quem desenvolve aplicações web em Java e pretende usar Ajax, tem a vida facilitada com a utilização do DWR (Direct Web Remoting).
Resumidamente, o DWR permite, de uma maneira muito simples, usar Javascript no cliente para interagir directamente com os objectos
Java no servidor e tem vindo a tornar-se um standard neste tipo de tecnologia.
Para aqueles que usam o Netbeans como IDE, a vida fica agora (ainda) mais facilitada com o lançamento (não oficial) da primeira release de um plugin para DWR, disponível aqui.
Esta primeira release permite já editar visualmente o ficheiro de configuração do DWR (embora só para operações de create e new) e o drag & drop dos objectos exportados directamente para as JSPs!
Estão previstas para as próximas releases funcionalidades como a exposição de form beans de Struts e managed beans de JSF e de um editor para Signatures.
promete!