quarta-feira, 27 de maio de 2009

t:commandLink não funciona

Problema aparentemente não incomum mas comum para muitos. Adicionamos um t:commandLink na página e quando clicamos nele (depois de compilada e gerado HTML) não funciona. Isso pode acontecer por diversos motivos mas consideremos aqui apenas um cenário.

Cenário do meu problema:

Tudo funcionava muito bem até que um belo dia resolvi atualizar meu Tomahawk para poder utilizar algumas novidades atraentes a mim, como por exemplo o headercolspan do t:column. Eu utilizava a versão 1.1.5 e estava atualizando para mais recente 1.1.8. Troquei a lib rodei o projeto e Voilá!! Um t:commandLink que ficava no meu template parou de funcionar. Primeiro deu raiva, depois ódio, mas logo me acalmei e mandei todo o projeto para um lugar nada agradável para qualquer um.

Pensei um pouco e cheguei a conclusão de que o problema era o Myfaces como um todo. Ainda utilizava a versão 1.1.5 então resolvi updateala também para a versão mais nova 1.2.6. Finalmente percebi que o problema persistia e minha paciência não.

Resolvi dar uma olhada mais cuidadosa no erro através do firebug:
form is undefined
oamSetHiddenInput("linkDummyForm", "autoScroll", "0,0")principal.jsf (linha 25)
oamSubmitForm("linkDummyForm", "j_id5", undefined, undefined)principal.jsf (linha 63)
onclick(click clientX=290, clientY=86)w0aQNeZT...Jzg%3D%3D (linha 2)
if(typeof form.elements[name]=='undefined')
Pensei Que diabos é isso? Funcionava antes! Fui olhar o código javascript gerado pelo tomahawk:
var form = document.forms[formname];
if(typeof form.elements[name]=='undefined')
{
var newInput = document.createElement('input');
newInput.setAttribute('type','hidden');
newInput.setAttribute('id',name);
newInput.setAttribute('name',name);
newInput.setAttribute('value',value);
form.appendChild(newInput);
}
else
{
form.elements[name].value=value;
}

Debugando o código notei que seria obrigatório colocar o t:commandLink dentro de um form (ou h:form). Alguém pode até estar se perguntando porque eu não coloquei o componente dentro de um form. A resposta é simples, eu não precisava colocar porque eu não precisava de um form naquela região do template. Lembrando a todos que um template é, normalmente, usado para moldar as páginas do projeto (ou parte delas). O template no JSF é como o master page do .Net.

Solução:

Colocar o t:commandLink dentro de um form ou h:form (meu caso), como no código abaixo:
<h:form>
<t:commandlink action="#{meuBean.acao}">Link no template</t:commandlink>
</h:form>
Finalmente mais um problema solucionado e post lançado!!

terça-feira, 26 de maio de 2009

IWAE0006E Archive is not a valid WAR File because the deployment descriptor can not be found (case sensitive): WEB-INF/web.xml

Esse é um problema que me atormentou por algumas horas até que criei coragem para procurar a solução. É um problema não muito comum mas alguns desenvolvedores já tiveram esse problema.

O problema:

Estamos no meio de um projeto (WEB) e queremos gerar um WAR através do export do Eclipse. Selecionamos a opção, o tipo da exportação (WAR), um nome e o destino onde será criado o arquivo. Click no botão Finish e surge então o nosso pesadelo (pra mim não mais), um popup (alert ou qualquer outra coisa que enche o saco). Vamos para o erro:

org.eclipse.core.runtime.CoreException: Extended Operation failure: org.eclipse.jst.j2ee.internal.web.archive.operations.WebComponentExportOperation
at org.eclipse.wst.common.frameworks.internal.datamodel.ui.DataModelWizard.performFinish(DataModelWizard.java:182)
at org.eclipse.jface.wizard.WizardDialog.finishPressed(WizardDialog.java:680)
at org.eclipse.jface.wizard.WizardDialog.buttonPressed(WizardDialog.java:355)
at org.eclipse.jface.dialogs.Dialog$3.widgetSelected(Dialog.java:660)
at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:90)
at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:66)
at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1085)
at org.eclipse.swt.widgets.Display.runDeferredEvents(Display.java:3180)
at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:2856)
at org.eclipse.jface.window.Window.runEventLoop(Window.java:820)
at org.eclipse.jface.window.Window.open(Window.java:796)
at org.eclipse.ui.actions.ExportResourcesAction.run(ExportResourcesAction.java:180)
at org.eclipse.ui.actions.BaseSelectionListenerAction.runWithEvent(BaseSelectionListenerAction.java:168)
at org.eclipse.jface.action.ActionContributionItem.handleWidgetSelection(ActionContributionItem.java:539)
at org.eclipse.jface.action.ActionContributionItem.access$2(ActionContributionItem.java:488)
at org.eclipse.jface.action.ActionContributionItem$5.handleEvent(ActionContributionItem.java:400)
at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:66)
at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1085)
at org.eclipse.swt.widgets.Display.runDeferredEvents(Display.java:3180)
at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:2856)
at org.eclipse.ui.internal.Workbench.runEventLoop(Workbench.java:1930)
at org.eclipse.ui.internal.Workbench.runUI(Workbench.java:1894)
at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:422)
at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:149)
at org.eclipse.ui.internal.ide.IDEApplication.run(IDEApplication.java:95)
at org.eclipse.core.internal.runtime.PlatformActivator$1.run(PlatformActivator.java:78)
at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:92)
at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:68)
at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:400)
at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:177)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at org.eclipse.core.launcher.Main.invokeFramework(Main.java:336)
at org.eclipse.core.launcher.Main.basicRun(Main.java:280)
at org.eclipse.core.launcher.Main.run(Main.java:977)
at org.eclipse.core.launcher.Main.main(Main.java:952)
org.eclipse.core.runtime.CoreException[0]: org.eclipse.core.commands.ExecutionException: Error exportingWar File
at org.eclipse.jst.j2ee.internal.archive.operations.J2EEArtifactExportOperation.execute(J2EEArtifactExportOperation.java:97)
at org.eclipse.wst.common.frameworks.internal.datamodel.DataModelPausibleOperationImpl$1.run(DataModelPausibleOperationImpl.java:376)
at org.eclipse.core.internal.resources.Workspace.run(Workspace.java:1737)
at org.eclipse.wst.common.frameworks.internal.datamodel.DataModelPausibleOperationImpl.runOperation(DataModelPausibleOperationImpl.java:401)
at org.eclipse.wst.common.frameworks.internal.datamodel.DataModelPausibleOperationImpl.runOperation(DataModelPausibleOperationImpl.java:352)
at org.eclipse.wst.common.frameworks.internal.datamodel.DataModelPausibleOperationImpl.doExecute(DataModelPausibleOperationImpl.java:242)
at org.eclipse.wst.common.frameworks.internal.datamodel.DataModelPausibleOperationImpl.executeImpl(DataModelPausibleOperationImpl.java:214)
at org.eclipse.wst.common.frameworks.internal.datamodel.DataModelPausibleOperationImpl.cacheThreadAndContinue(DataModelPausibleOperationImpl.java:89)
at org.eclipse.wst.common.frameworks.internal.datamodel.DataModelPausibleOperationImpl.execute(DataModelPausibleOperationImpl.java:202)
at org.eclipse.wst.common.frameworks.internal.datamodel.ui.DataModelWizard$1$CatchThrowableRunnableWithProgress.run(DataModelWizard.java:211)
at org.eclipse.jface.operation.ModalContext$ModalContextThread.run(ModalContext.java:113)
Caused by: org.eclipse.jst.j2ee.commonarchivecore.internal.exception.SaveFailureException: Error opening archive for export..
at org.eclipse.jst.j2ee.internal.web.archive.operations.WebComponentExportOperation.export(WebComponentExportOperation.java:48)
at org.eclipse.jst.j2ee.internal.archive.operations.J2EEArtifactExportOperation.execute(J2EEArtifactExportOperation.java:89)
... 10 more
Caused by: org.eclipse.jst.j2ee.commonarchivecore.internal.exception.OpenFailureException: IWAE0006E Archive is not a valid WAR File because the deployment descriptor can not be found (case sensitive): WEB-INF/web.xml
at org.eclipse.jst.j2ee.commonarchivecore.internal.impl.CommonarchiveFactoryImpl.openSpecificArchive(CommonarchiveFactoryImpl.java:805)
at org.eclipse.jst.j2ee.commonarchivecore.internal.impl.CommonarchiveFactoryImpl.openWARFile(CommonarchiveFactoryImpl.java:841)
at org.eclipse.jst.j2ee.internal.web.archive.operations.WebComponentExportOperation.export(WebComponentExportOperation.java:42)
... 11 more


Gostou neh? Então, primeiro saibamos analisar um erro. Em um stack trace como este a primeira coisa que olhamos é a mensagem do último erro. Último erro? Mas não é só um? Um stack trace é realmente um erro apenas mas que pelo voltar das classes acaba sendo capturado por algum try catch que lança outra exceção baseada naquela que acaba sendo capturada por outro try catch até que um belo dia o erro chega, finalmente, na nossa telinha, ou no main do sistema.

Caused by: org.eclipse.jst.j2ee.commonarchivecore.internal.exception.OpenFailureException: IWAE0006E Archive is not a valid WAR File because the deployment descriptor can not be found (case sensitive): WEB-INF/web.xml
at org.eclipse.jst.j2ee.commonarchivecore.internal.impl.CommonarchiveFactoryImpl.openSpecificArchive(CommonarchiveFactoryImpl.java:805)
at org.eclipse.jst.j2ee.commonarchivecore.internal.impl.CommonarchiveFactoryImpl.openWARFile(CommonarchiveFactoryImpl.java:841)
at org.eclipse.jst.j2ee.internal.web.archive.operations.WebComponentExportOperation.export(WebComponentExportOperation.java:42)
... 11 more
O texto em negrito é o tema de nosso post de hoje. Pesquisando um pouco, como sempre no Google, chegamos na fonte do problema. Ele ocorre quando uma pasta src é definida para deploy mas não contêm nada dentro. E como vejo onde isso está definido isso? Simples, no arquivo:
meuProjeto/.settings/org.eclipse.wst.common.component
Isso é um arquivo XML, basta abrí-lo para termos algo parecido com isso:
<?xml version="1.0" encoding="UTF-8"?>
<project-modules id="moduleCoreId" project-version="1.5.0">
<wb-module deploy-name="jsftemplate">
<wb-resource deploy-path="/" source-path="jsf-template"/>
<wb-resource deploy-path="/WEB-INF/classes" source-path="/WEB-INF/src"/>
<property name="java-output-path" value="build/classes"/>
<property name="context-root" value="jsftemplate"/>
</wb-module>
</project-modules>
Alguns arquivos podem variar um pouco dependendo do projeto. O nosso problema aqui são as seguintes linhas:
<wb-resource deploy-path="/" source-path="jsf-template"/>
<wb-resource deploy-path="/WEB-INF/classes" source-path="/WEB-INF/src"/>
wb-resource serve para informar os sources do nosso projeto. deploy-path informa onde será feito o deploy. E o source-path mostra onde estão os sources que serão deployados. Antes de realizar qualquer alteração no arquivo verifique essas pastas definidas no xml, depois exclua as linhas que apontam para pastas vazias ou inexistentes. No meu caso foi a seguinte:
<wb-resource deploy-path="/" source-path="jsf-template"/>
Apague a linha, salve o arquivo, abra o Eclipse novamente e tente exportar seu arquivo WAR. Pronto, mais uma solução simples que possui agora mais um resultado na busca do Google.

fonte: http://www-01.ibm.com/support/docview.wss?uid=swg21255956

sexta-feira, 22 de maio de 2009

[JSF] Tomahawk: java.lang.IllegalStateException: ExtensionsFilter not correctly configured ....

Quem nunca passou por isso antes? Talvez a maioria, mas eu passei por isso a três anos atrás e hoje novamente. Na primeira vez eu resolvi mas não fazia a mínima de como, talvez tenha desistido do que estava fazendo e pronto. Mas na segunda vez tomei a iniciativa de publicar a solução, que é bastante simples.

As primeiras linhas do erro:
java.lang.IllegalStateException: ExtensionsFilter not correctly configured. Resource mapping missing. Resources cant be delivered. Please see: http://myfaces.apache.org/tomahawk/extensionsFilter.html
at org.apache.myfac...ory.throwExtensionsFilterMissing(AddResourceFactory.java:371)
at org.apache.myfac...AddResourceFactory.checkEnvironment(AddResourceFactory.java:352)
at org.apache.myfac...AddResourceFactory.getInstance(AddResourceFactory.java:288)
at org.apache.myfaces...Renderer.encodeEnd(HtmlTabbedPaneRenderer.java:88)
at javax.faces.compon...tBase.encodeEnd(UIComponentBase.java:536)
at com.sun.facelets...nentSupport.encodeRecursive(ComponentSupport.java:242)
Talvez minha inexperiência em JSF na época fossem o motivo para não ter encontrado a resposta, hoje quando procurei no Google achei até meu post de dúvida no GUJ de 2006, sem respostas obviamente. Mas hoje a história foi diferente, corri atrás dos erros analizando-os cuidadosamente.

O cenário do erro:

Desenvolvendo, feliz da vida, algumas telinhas cheias de controles e umarruma de requisições JSF cheguei a uma necessidade de ter um popup. Nada de complicado até agora, qualquer faz um popup seja com divs, outras janelas etc, mas dentro delas haveriam várias telas. Mas uma situação comum em nossas vidas de desenvolvedores. Pensei "Se são várias, mas não apareceram simultâneamente então vou usar tabs". E lá fomos nós de um componente JSF para isso. Existem vários mas como estamos utilizando o Tomahawk aqui então pegamos o t:panelTabbedPane juntamente como o t:panelTab. Montamos a tela e então .... o erro.

Actually um outro desenvolvedor que estava cuidando desta tela e, sabiamente, ao dar de cara com o erro foi procurou o sábio mestre dos mestres Google. Passeando pelas suas vias de sabedoria me procurou, me mostrou o erro e eu disse "Eu já vi isso antes, mas não lembro como resolve. Procura no Google!" E ele respondeu "Já procurei, olha o que eu achei!" e em seguida me mostrou o meu post no GUJ com a mesma dúvida. Pensei logo em chamar o Chapolin Colorado mas lembrei que ele já está bem velho, então fui lê o erro novamente, com mais atenção.

Localizando a solução:

Logo na primeira linha do erro vemos:
ExtensionsFilter not correctly configured. Resource mapping missing. Resources cant be delivered. Please see: http://myfaces.apache.org/tomahawk/extensionsFilter.html
Verifiquem o trecho em negrito. É lá que conseguiremos a solução para todos os nossos problemas. Lá ele diz logo para mapear o ExtesionFilter do MyFaces. Procurei no meu web.xml e já estava mapeado. Passei mais alguns minutos e caiu a ficha (ou o crédito do cartão). No meu arquivo de configuração tinha:
<filter-lass>org.apache.myfaces.component.html.util.ExtensionsFilter</filter-class>
E no site informado tinha:
<filter-class>org.apache.myfaces.webapp.filter.ExtensionsFilter</filter-class>
Pensei, puxa como os outros são burros por não terem visto que era diferente (claro que eu não ia me acusar). Troquei mas o erro continuou .... As pessoas realmente não aprendem, sempre temos que ler uma solução até o fim. Ainda faltava um filter-mappping:
<filter-mapping>
<filter-name>MyFacesExtensionsFilter</filter-name>
<url-pattern>*.jsf</url-pattern>
</filter-mapping>
Adicionei ao web.xml, reiniciei o Container Web e Voilá!! Tudo funcionou bem bonitinho.

Essa é apenas mais uma solução, simples, para mais um problema, simples.

Esta foi outra lição toon
E se você pouco sabia
Caminhando pelo mundo
Vai aprender o que não queria!

Fonte: http://myfaces.apache.org/tomahawk/extensionsFilter.html

Fazendo :hover, :active (e outros) funcionar no IE7

Para estrear um blog que não tem tema nenhum nada como um assunto qualquer para começá-lo. Este é apenas um pixel na guerra dos designers contra as incompatibilidades do IE com o padrão CSS definido pelo W3C.

Todo designer, que se preza, sabe que uma coisa que O Internet Explorer (browser preferido dos leigos) não segue os padrões internacionais, pelo contrário, cria seus próprios padrões a cada versão e nas vezes são incompatíveis até mesmo entre elas. Well, hoje me deparei com um problema desses, na verdade não fui eu mas acabei caindo no problema da mesma forma. No CSS tínhamos algo como:
input:hover { background-color: #000; color: #FFF; }

O código estava em arquivo CSS comum como outro qualquer e, para minha alegria, funcionava perfeitamente no Firefox (browser preferido de quem sabe pra que serve um padrão :D), mas no IE7 era como se nem existisse. Pensei comigo mas isso vai de encontro à filosofia do "Que o IE exploda juntamente com seus usuários Software Livre já que ela, em algum ponto, diz que o usuário deve ter a oportunidade de escolher um programa dentre vários para resolver seu problema, e como a maioria (forçadamente) ainda escolhe o IE então temos, como obrigação de desenvolvedores, prover soluções para isso.

Pesquisando um pouquinho verifico uma série de soluções que nunca funcionam, a maioria delas em fórum que, em geral, aparecem muitas soluções que não solucionam nada. Aparentemente os usuários de fóruns gostam de postar a primeira solução que aparece no Google esquecendo, muitas vezes, de ler a dúvida toda e verificando que a solução que quer postar só funcionam para alguns cenários mas não para todos, principalmente o exposto pelo prejudicado. As solução iam desde códigos Javascript à instaladores de outros browsers que os usuários teriam que executá-los sempre que executassem a página com o problema proferido no começo deste post.

Até que, finalmente, um felizardo encontra uma solução e me felicito ao ver que não preciso de nenhuma gambiarra para solucionar tal problema. Segundo o autor só é preciso adicionar o seguinte código como primeira linha do arquivo:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">

Eu vi, e pensei "Só isso?" e a resposta era positiva. Pelo menos para o :hover. Mas analizemos para que serve esta linha:
DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"

Aqui informamos para o browser que tipo de documento ele está formatando, neste caso um HTML dentro dos padrões W3C e na versão 4.01
"http://www.w3.org/TR/html4/strict.dtd"
Esta é a parte mais importante da solução, é este DTD que vai informar que atributos e tags são suportados na sua página. Para maiores detalhes (http://www.w3schools.com/tags/tag_DOCTYPE.asp)

A solução realmente funciona para o :hover e, felizmente, outro felizardo encontrou o restante da minha solução. Na verdade o página não tem o propósito de mostrar a solução mas o código fonte dela a possui. A diferença da solução anterior é apenas:
  • "http://www.w3.org/TR/html4/loose.dtd"
ao invés de
  • "http://www.w3.org/TR/html4/strict.dtd"
Isso já traz algumas diferenças para o resultado da página. Acredito que para um bom entendendor isto já basta de respostas, pelo menos para o problema proposto. Quem sabe futuramente escrevo algo mais educativo sobre os DOCTYPEs existentes. Para quem não conseguiu o efeito desejado ainda teste os seguintes DTDs:
  • <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
  • <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
  • <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">
  • <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
  • <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  • <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">
  • <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
Se encontrar sua solução publique-a de alguma forma (blogs, fóruns, comments etc). A maior dificuldade de encontrar uma solução na internet é que muitos as tem mas poucos compartilham a informação. Estou começando a fazer minha parte.

Fontes:
http://www.bernzilla.com/item.php?id=762
http://meyerweb.com/eric/css/tests/css2/sec05-11-03.htm
http://www.w3.org/TR/html4/struct/global.html#h-7.1
http://www.w3schools.com/tags/tag_DOCTYPE.asp