Thursday, May 17, 2007

Classpath conflicts

One problem kept bugging me for three weeks. It was quite irrational - I couldn't extend the functionality of the application, because changes in services (that were EJB3 session beans) were not visible. The problem occurred when deploying to embedded OC4J, but, surprisingly, not when deploying to OC4J standalone...

After getting lots of JBO-25221 (method not supported) and java.lang.NoSuchMethodError I thought I would investigate the class used at runtime. I found that the method that should be there wasn't there at runtime. So I decided to find out where did the class come from. Bertrand Delacretaz has an example of checking when was a Java class compiled. Small modifications to his example and the mysterious class is unmasked:

MyClass.class.getResource( "MyClass.class" ).openConnection().getURL().toString()


I was shocked when I saw the output - it turned out that another project that should have a small 2-class jar in my application had packed also a bunch of classes from the same model. The model was referenced by both projects as a dependency and the deployment profile had a magic checkbox selected:



That resulted in a silent classpath conflict I was not aware of... Finally, the problem is gone :)

Tuesday, May 15, 2007

... is not a registered tag in that namespace?

Today JDeveloper broke my project. I don't know what exactly happened, but putting an ADF Faces tag inside a page resulted in JSP compilation errors, e.g.:

Error: http://xmlns.oracle.com/adf/faces:panelPage is not a registered tag in that namespace.


After a few minutes of trying different things I opened the web.xml file and found the problem:

<taglib>
<taglib-uri>
http://java.sun.com/jsf/core
</taglib-uri>
<taglib-location>
/WEB-INF/lib/jsf-impl.jar
</taglib-location>
</taglib>
<taglib>
<taglib-uri>
http://xmlns.oracle.com/adf/faces
</taglib-uri>
<taglib-location>
/WEB-INF/lib/adf-faces-impl.jar
</taglib-location>
</taglib>
<taglib>
<taglib-uri>
http://xmlns.oracle.com/adf/faces/html
</taglib-uri>
<taglib-location>
/WEB-INF/lib/adf-faces-impl.jar
</taglib-location>
</taglib>


Now that was confusing... Removing that piece of code helped. But how did it appear here? JDev only knows...

One OT to mention here: the code you see above was escaped (to be displayed properly by Blogger) using a very simple and effective tool I've come across recently. Check out Dietmar Aust's blog.

Friday, May 11, 2007

Extending ADF's FacesPageLifecycle

Recently I've encountered strange exceptions while testing my application. I pressed the Submit button and sometimes everything was fine, while at times the action bound to the button was not invoked, but errors were reported:

JBO-29000: null
java.lang.NullPointerException


Unfortunately, there was no trace of this exception and I didn't even know where should I look for it. Then I came up with and idea: let's decorate the process of displaying errors and find out more about this exception. To achieve this, I had to extend ADF's FacesPageLifecycle:

package januszm.adf.util;

import javax.faces.context.FacesContext;

import oracle.adf.controller.faces.lifecycle.FacesPageLifecycle;

import oracle.binding.AttributeBinding;

public class MyPageLifecycle extends FacesPageLifecycle {
protected void addMessage(FacesContext context, AttributeBinding binding,
Throwable error) {
super.addMessage( context, binding, error );
// the easiest thing we can do here
error.printStackTrace();
}
}


Another question is: how can we tell ADF to use our lifecycle? That's easy too - we extend ADFPhaseListener:

package januszm.adf.util;

import oracle.adf.controller.faces.lifecycle.ADFPhaseListener;
import oracle.adf.controller.v2.lifecycle.PageLifecycle;


public class MyPhaseListener extends ADFPhaseListener {
protected PageLifecycle createPageLifecycle() {
return new MyPageLifecycle();
}
}


Finally, we need to substitute the original ADFPhaseListener with our class in faces-config.xml:

<lifecycle>
<phase-listener>januszm.adf.util.MyPhaseListener</phase-listener>
</lifecycle>


That allowed me to locate the problem - the exception was thrown in the line containing:

this.someField.toString()


because sometimes the field wasn't set...

The conclusion is simple - if a library throws a weird exception, don't blame the library, but check YOUR code first.

Tuesday, May 8, 2007

Saturday, May 5, 2007

Checking if passwords are equal in ADF

After spending some time trying to get Tomahawk's validateEqual working in ADF Faces (works perfectly in JSF, though) I've decided to implement a custom validation method. The second af:inputText wasn't connected to any ADF binding and this turned out to be the main troublemaker of the day. With the help of the Dev Guide validation started working quickly, but I've encountered a bunch of annoying little problems...

1. The error message would not display as expected. I expected something like "Repeat password - Passwords are not equal", but all I got was "com.sun.faces.el.ValueBindingImpl@9af3ee - Passwords are not equal". Fortunately, a quick forum search saved me this time (namely this thread and this one).

2. Both af:inputText components did not behave as expected after validation failure. JSF h:inputSecret has a redisplay attribute, but I didn't notice anything similar for af:inputText. When secret was set to true, something weird happened - after each validation failure the value of both components was initially set to ******, but it disappeared after pressing a key inside the component. After digging for a while I still don't know why the initial value is set to six stars, but I found the function that removes them:

function _clearPassword(a0,a1)
{
if(window.event!=(void 0))
a1=window.event;
if(a0.value!="******")
return true;
if((a1.keyCode==8)||
((a1.keyCode>=46)&&(a1.keyCode<112)))
a0.value="";
return true;
}


Now that's interesting... Anyway, I tried to get rid of those stars for a long time. The first component that was bound to the password attribute was easier, because binding the component to the backing bean that performs validation and setting the value to null did the trick.

The second component kept setting its value to ****** despite setting it to null in the backing bean. After many unsuccessful attempts I removed those stars with a small JavaScript workaround inside the afh:body tag:

onload="_getElementById(document, 'repeatedUserPassword').value = '';"