Monday, 1 November 2010

Persisting ViewScoped beans across views in request

I came across this problem when trying to deal with view scoped beans that you want to reuse in a different view, i.e. after clicking next in a wizard like page.

This was originally Discussed Here (now removed since Oracle merged Sun's forums).

The View Scope
There is a a brief summary of it here: JSF 2.0 New Feature Preview Series (Part 5) EDR1 Potpourri.
Basically view scoped values are attached to the current viewId. Values for the view scoped variables are stored in the map returned by UIViewRoot.getViewMap(boolean). Which is great for when your accessing the same view. However, it is cleared when the view is changed (e.g. from a navigation result) FacesContext.setViewRoot(javax.faces.component.UIViewRoot)

However, the flash scope was designed seemingly to solve this very problem.

Using the Flash Scope
Here's a write up of the Understanding the JSF 2.0 Flash scope. There's also some caveats shown here: The benefits and pitfalls of @ViewScoped.
So the logical step is to try it, :
public String doIt() {
   FacesContext.getCurrentInstance().getExternalContext().getFlash().put("thisBean", this);   return "next";
}
However, after some poking around, I discovered you can't re-store in FlashScope as ManagedBeanELResolver is before ScopedAttributeELResolver in the expression language resolver chain. Meaning that if you have a view "view1" with bean name "viewScopeBean1" and want to use the exact same bean in "view2" with bean name "viewScopeBean2", you can't.
The most you can do is store whatever data you need in the flash scope, then in the @PostConstruct of the new bean, pull in the values you need. Not an elegant solution but I'm sure messing around with the ELResolver chain could also do it but then you run the risk of it breaking else where.

Tuesday, 12 October 2010

f:convertNumber parses to double not BigDecimal

The default behaviour of the f:convertNumber, implemented with NumberConverter, in converting a decimal String of "1.11", is to convert it to a double and then JSF converts that to a BigDecimal. Most of the time you won't notice this happening until you use CallableStatement.setBigDecimal to invoke a stored procedure. In MS SQL Server, you get the following error:
com.microsoft.sqlserver.jdbc.SQLServerException: Error converting data type nvarchar to decimal.
The cause of this is because inside the BigDecimal it stored "1100000000000000976996261670137755572795867919921875" as a result of converting the String to a double. This floating point error is the motivation for using BigDecimal in the first place. And of course because the SQL Server JDBC driver didn't know how to handle the BigDecimal properly.

Looking into ConvertNumber, it uses one of two converters (DecimalFormat or NumberFormat) depending on whether 'pattern' is used. In Java 5, they added setParseBigDecimal(boolean newValue) in order to yield the desired output.

You can't easily change the NumberConverter but you can very simply add your own. To do so, I grabbed the source of NumberConverter from Mojarra 2.0.2 and copied all of it into my own class: net.devgrok.BigDecimalConverter.
@FacesConverter(forClass = BigDecimal.class, value="bigdecimal")
public class BigDecimalConverter implements Converter, PartialStateHolder {

// ... existing code

  public Object getAsObject(FacesContext context, UIComponent component, String value) {

// ... existing code

      // Create and configure the parser to be used
      parser = getNumberFormat(locale);
      if (((pattern != null) && pattern.length() != 0) || "currency".equals(type))
      {
        configureCurrency(parser);
      }
      parser.setParseIntegerOnly(isIntegerOnly());
      boolean groupSepChanged = false;

      // BEGIN BigDecimal HACK
      if (parser instanceof DecimalFormat)
      {
        ((DecimalFormat) parser).setParseBigDecimal(true);
      }
      // END BigDecimal HACK
      
// ... existing code

  }

// ... existing code

}
Then just create a file META-INF/custom.taglib.xml:
<?xml version="1.0" encoding="UTF-8"?>

<facelet-taglib xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facelettaglibrary_2_0.xsd"
    version="2.0">

    <namespace>http://devgrok.net/facestags</namespace>

    <tag>
        <tag-name>convertBigDecimal</tag-name>
        <converter>
            <converter-id>bigdecimal</converter-id>
        </converter>
    </tag>

</facelet-taglib>

And to use:
<?xml version="1.0" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
  xmlns:ui="http://java.sun.com/jsf/facelets"
  xmlns:h="http://java.sun.com/jsf/html"
  xmlns:f="http://java.sun.com/jsf/core"
  xmlns:fn="http://java.sun.com/jsp/jstl/functions"
  xmlns:c="http://java.sun.com/jsp/jstl/core"
  xmlns:dg="http://devgrok.net/facestags"
  >

  from goes here etc
  
  <h:inputText id="txtPrice" value="#{bean.item.price}">
    <dg:convertBigDecimal pattern="##0.00" />
  </h:inputText>
  
</ui:composition

Sunday, 19 September 2010

Fixing my HTC Desire's wifi

I recently did an Over The Air (OTA) update of my new HTC Desire from 2.09 to 2.10. The update all seemed to run correctly. However I soon noticed that the wifi wasn't working. Looking in settings, it showed wifi was disabled and when you try and enable it, you just get "error". Seems like other people have been getting this as well. I tried following their advice, reset the factory defaults but that didn't work. I couldn't get the recovery mode to load (in order to wipe the caches) - after some brief searching it appeared that this was due to root access so I gave up on that.
NOTE: it turns out I was doing it wrong. After selecting the recovery option a red triangle with an exclamation point is show. At which point you need to press volume+ and power to show the next menu.

Next I tried using latest RUU (2.10) but got Error 170 Usb Connection Error. One post mentions under 'update':
HTC has released a new Froyo OTA update. You can install this directly as an RUU. You may need the latest version of HTC Sync for this version. There appear to be a lot of issues installing this RUU due to drivers. I'd recommend you install the earlier version and update OTA to this version (you can do this manually too).

Using 1.21.405.2 gave me ERROR [131] customer id error. After some poking around, turns out that branded phones don't have a CID... That's what you get with a phone from Hong Kong I guess. However I'm not really sure if that's true...

Eventually I came across this thread which mentions fixing the wifi error with this update: Froyo (2.09.405.8) update - Windows not required. It all installed correctly and after starting it up, it ran through the first-time setup and this time said it detected a wireless network. Success! I didn't try the windows RUU for 2.09 so I'm not sure if that will work.

Next I downloaded the update manually as per the instructions here. The phone started downloading OTA_Bravo_HTC_EU_2.10.405.2-2.09.405.8_release567cypyi0feo4th5.zip so I'm using that instead of the one in the post.

This thread has a better discussion of the problem, with some simple instructions on how to revert and reload when a goldcard isn't needed.

Friday, 17 September 2010

Escaping a regex expression

I was working on a project where I needed to dynamically generate a regular expression based on some user input but the string to search for needed to be escaped. Below is the quick and dirty way to do it:
/**
 * Utility function to replace regular expressions.
 *
 * @param input
 * @return
 */
private static String escapeRegex(String input)
{
    String special[] = { "\\", "[", "]", "^", "$", ".", "|", "?", "*", "+", "(", ")" };
    String val = input;
    for (String replace : special)
    {
        val = val.replace(replace, "\\" + replace);
    }

    return val;
}

Tuesday, 14 September 2010

Tomcat DBCP: Data source is closed

After updating to the latest version of Tomcat I started getting the following error when redeploying a webapp from inside eclipse:
java.sql.SQLException: Data source is closed
    at org.apache.tomcat.dbcp.dbcp.BasicDataSource.createDataSource(BasicDataSource.java:1362)
    at org.apache.tomcat.dbcp.dbcp.BasicDataSource.getConnection(BasicDataSource.java:1044)

It seemed to be related to an updated version of DBCP that is used inside tomcat (I get Tomcat to manage my datasource). After tracing the code a bit I discovered it was because I called this, in my Filter.destroy() to do clean up:
((BasicDataSource) dataSource).close();

Older versions of Tomcat (6.0.26 and below), didn't care if you called it. When the webapp was reloaded, the datasource still worked. But as of 6.0.28, the newer version of DBCP seems to cause problems.

So if you want to clean up after the webapp, use the removeAbandoned attribute in the Tomcat Configuration on the datasource, that way DBCP just cleans up after itself.

NOTE: I'm using the tomcat-dbcp.jar from Tomcat 7 in my Tomcat 6 instance so that I'm able to use the JDBC 4 functionality. Hence the link to the new documentation, Tomcat 6 Configuration also has it though.

Wednesday, 8 September 2010

Portable java environments on windows

I always seem to be copying my development environment between PCs and latops which end up being time consuming. Its not the size of the data, its the number of small files (mainly javadoc & java source) that slow the whole process down. I already store the source code and workspace for my clients in a TrueCrypt Volume, to prevent my code being stolen if someone were to get hold of my laptop.

All the other necessary files I need for development; such as libraries, jdk, tomcat/jboss, elcipse etc; are stored on my hard drive. In some cases on different drive letters on different computers. So to simplify this I wanted to store them in a similar way to a TrueCrypt volume but without the overhead of encryption and the hassle of mounting it. After some poking around I remember that Windows 7 came with built-in support for creating and mounting Virtual Hard Disk (VHD) straight from the Storage Manager. The VHD are used by Virtual PC. For those running Vista, Virtual PC 2007 should support this.

For windows 7, you can easily mount an image using the admin interfaces or even create a shourtcut/bat script to mount it. However I wanted the image mounted on start up so it was ready to use. I eventually came across this neat utility (vhdattach) to add shell extension to mount an image with the option to mount at startup. Vista has this alternative.

Now all I need to do to swap PC's is to copy my TrueCrypt volume and my VHD across, mount them on the same letters and done! No need to change paths etc.

Sunday, 6 June 2010

EL Coerces empty/null to ZERO

I recently came across a problem where I was expecting a null value to be populated into one of my JSF managed beans on submit. After digging through the entire stack, I found out this was deliberate.
This bug was introduced into Tomcat 6.0.16, as a consequence of implementing the EL spec to the letter.
The option to disable this change at command line was added in 6.0.17:
-Dorg.apache.el.parser.COERCE_TO_ZERO=false

A patch in Tomcat 6.0.24 was added to allow you to change it at run time:
System.getProperties().put("org.apache.el.parser.COERCE_TO_ZERO", "false");

This problem also directly occurs in JSF, as it uses an EL engine as well. There's a patch for 1.2_08-b01 but it is happening in 2.0.2. But the above fix seems to work.

Request to change the JSP Spec
As per chapter 1.18.2 and 1.18.3 of EL spec, EL coerces null values to non-null
values such as empty String or (Long) 0 or (Boolean) false. This behaviour is in
almost all cases unwanted when the type is not a primitive.
After some more digging also came across this discussion at stackoverflow.

Tuesday, 1 June 2010

A simple way to Inject Guice into JSF (Mojarra)

Previously I have been using GuiceSF to inject the JSF managed beans which extends the Mojarra's implementation of ELResolver (ManagedBeanELResolver). This was bit of a hack but it was relatively simple to implement.
In upgrading to newer version of JSF, I had to manually tweak it to keep it in line with the new features in the spec which took a fair amount of debug stepping. So when JSF 2 came out I looked around for a way to do it which still used the implementation's bean creation and handling but also injected it with Guice.

After digging around in the injection code in Mojarra, I saw how simple it would be to extend the provided Tomcat injection (which injects stuff for the @Resource and @EJB etc annotations).

This solution first requires the injection provider be specified in web.xml:
<context-param>
   <description>JSF Injection provider</description>
   <param-name>com.sun.faces.injectionProvider</param-name>
   <param-value>net.devgrok.jsf.Tomcat6GuiceInjectionProvider</param-value>
</context-param>

Next extend the Tomcat injector:
package net.devgrok.jsf;

import javax.faces.FacesException;
import javax.servlet.ServletContext;

import org.apache.AnnotationProcessor;

import com.google.inject.Injector;
import com.sun.faces.spi.InjectionProviderException;
import com.sun.faces.vendor.Tomcat6InjectionProvider;

/**
 * Based on {@link com.sun.faces.vendor.Tomcat6InjectionProvider}. Injects using Guice for JSF.
 * 
 * @author Chris Watts
 */
public class Tomcat6GuiceInjectionProvider extends Tomcat6InjectionProvider
{

 private ServletContext servletContext;

 public Tomcat6GuiceInjectionProvider(ServletContext servletContext)
 {
  super(servletContext);
  this.servletContext = servletContext;
 }

 public void inject(Object managedBean) throws InjectionProviderException
 {
  try
  {
   getProcessor().processAnnotations(managedBean);
   getInjector().injectMembers(managedBean);
  }
  catch (Exception e)
  {
   throw new InjectionProviderException(e);
  }
 }

 private AnnotationProcessor getProcessor()
 {
  return ((AnnotationProcessor) servletContext.getAttribute(AnnotationProcessor.class.getName()));
 }
 
 protected Injector getInjector()
 {
  Injector injector = (Injector) servletContext.getAttribute(Injector.class.getName());
  if (injector == null)
   throw new FacesException("Guice injector not found, verify your web.xml file for mapping GuiceContextListener");
  return injector;
 }
}

Tuesday, 11 May 2010

Converting custom JSF 1 components to JSF 2

These are just some observations/tips I made when converting my JSF 1 components to JSF 2.

Simpler .taglib.xml

The taglib definition changes from mycomponent.tld to mycomponent.taglib.xml. The xml inside the new file is similar but with some changes (i.e. <name> becomes <tag-name>). The major simplification is that attributes no longer need to be defined. Just the tag-name and component-type.
<tag>
 <name>menuItem</name>
 <tag-class>net.devgrok.component.MenuItemTag</tag-class>
 <body-content>JSP</body-content>
 <attribute>
  <name>value</name>
  <required>false</required>
  <deferred-value>
   <type>java.lang.String</type>
  </deferred-value>
 </attribute>
</tag>
Becomes:
<tag>
 <tag-name>menuItem</tag-name>
 <component>
  <component-type>net.devgrok.MenuItem</component-type>
 </component>
</tag>
And the newly created xml file is referenced as a context-param in web.xml (it could just be located under WEB-INF):
<context-param>
   <description>list of facelet libraries</description>
   <param-name>javax.faces.FACELETS_LIBRARIES</param-name>
   <param-value>/WEB-INF/classes/META-INF/custom.taglib.xml</param-value>
</context-param>

Component Class

The component class is annotated with: @FacesComponent(value = "value from mycomponent.taglib.xml"). Then for each property create a setter (I also create a getter) that sets the value as evaluated at the apply request values phase. I use an enum as the key to store attributes against.
@FacesComponent(value = "net.devgrok.MenuItem")
public class MenuItem extends UIComponentBase {
 private enum PropertyKeys {
  value;
  String toString;

  PropertyKeys(String toString)  {
   this.toString = toString;
  }

  PropertyKeys()  {
  }
 }
 
 public void setValue(Object value) {
  getStateHelper().put(PropertyKeys.value, value);
 }
 
 public Object getValue()
 {
  return getStateHelper().get(PropertyKeys.value);
 }

No need for tag handler

As facelets is fully integrated into JSF 2 and it is not driven by JSPs, the need to create a class for every component that extends com.sun.facelets.tag.TagHandler or javax.faces.webapp.UIComponentELTag has been removed. For cases where you need to perform special actions after the values have been populated, create a simple class extending javax.faces.view.facelets.ComponentHandler. There are some instructions here: JSF2 Facelet Tag Handlers.

Some problems

Had some trouble with tag attributes, which I worked around by using the above getter instead of using (String) this.getAttributes().get("name");

Wednesday, 17 February 2010

ClassNotFoundException destroying a session in Tomcat

When a session was being expired (either through a timeout, or via using the Tomcat Manager to manually destroy them) that contained an EJB I was getting a ClassNotFoundException. The stack trace when killing from the manager was:
java.lang.RuntimeException: Specified calling class, net.devgrok.MyEjbRemote could not be found for WebappClassLoader
  delegate: false
  repositories:
----------> Parent Classloader:
org.apache.catalina.loader.StandardClassLoader@70cb6009

 at org.jboss.ejb3.common.lang.SerializableMethod.getClassFromName(SerializableMethod.java:311)
 at org.jboss.ejb3.common.lang.SerializableMethod.getClassType(SerializableMethod.java:282)
 at org.jboss.ejb3.common.lang.SerializableMethod.toMethod(SerializableMethod.java:233)
 at org.jboss.ejb3.common.lang.SerializableMethod.toMethod(SerializableMethod.java:220)
 at org.jboss.ejb3.proxy.impl.handler.session.SessionProxyInvocationHandlerBase.invoke(SessionProxyInvocationHandlerBase.java:182)
 at org.jboss.ejb3.proxy.impl.handler.session.SessionProxyInvocationHandlerBase.invoke(SessionProxyInvocationHandlerBase.java:164)
 at net.devgrok.JndiRemoteProxyInvocationHandler.invoke(JndiRemoteProxyInvocationHandler.java:55)
 at $Proxy15.logout(Unknown Source)
 at org.apache.catalina.session.StandardSession.expire(StandardSession.java:702)
 at org.apache.catalina.session.StandardSession.expire(StandardSession.java:660)
 at org.apache.catalina.session.StandardSession.invalidate(StandardSession.java:1113)
 at org.apache.catalina.session.StandardSessionFacade.invalidate(StandardSessionFacade.java:150)
 at org.apache.catalina.manager.HTMLManagerServlet.invalidateSessions(HTMLManagerServlet.java:822)
 at org.apache.catalina.manager.HTMLManagerServlet.doSessions(HTMLManagerServlet.java:690)
 at org.apache.catalina.manager.HTMLManagerServlet.doGet(HTMLManagerServlet.java:128)
 at org.apache.catalina.manager.HTMLManagerServlet.doPost(HTMLManagerServlet.java:164)
 at javax.servlet.http.HttpServlet.service(HttpServlet.java:637)
 at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
 ...
 at java.lang.Thread.run(Thread.java:619)
Caused by: java.lang.ClassNotFoundException: net.devgrok.MyEjbRemote
 at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1387)
 at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1233)
 at java.lang.Class.forName0(Native Method)
 at java.lang.Class.forName(Class.java:247)
 at org.jboss.ejb3.common.classloader.PrimitiveAwareClassLoader.findClass(PrimitiveAwareClassLoader.java:105)
 at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
 at java.lang.ClassLoader.loadClass(ClassLoader.java:248)
 at org.jboss.ejb3.common.lang.SerializableMethod.getClassFromName(SerializableMethod.java:307)
 ... 32 more
After some investigating I discovered the current thread's ClassLoader was incorrect. So I created a HttpSessionListener to swap the ClassLoader, unload the session and put the ClassLoader back.
public class SessionLifecycleEventListener implements HttpSessionListener
{
   private static final Logger log = LoggerFactory.getLogger(SessionLifecycleEventListener.class);
   public void sessionDestroyed(HttpSessionEvent evt)
   {
      HttpSession session = evt.getSession();
      //remember class loader
      ClassLoader currentLoad = Thread.currentThread().getContextClassLoader();
      try
      {
         //set classloader to be the webapp's
         ClassLoader webApp = this.getClass().getClassLoader();
         Thread.currentThread().setContextClassLoader(webApp);
         
         //clear all the session objects now
         Enumeration<String> names = session.getAttributeNames();
         while (names.hasMoreElements())
         {
            String name = names.nextElement();
            session.removeAttribute(name);
         }
      }
      catch (RuntimeException e)
      {
         log.error("closeEjbSession caught exception", e);
      }
      finally
      {
         //restore classloader
         Thread.currentThread().setContextClassLoader(currentLoad);
      }
      
   public void sessionCreated(HttpSessionEvent evt)
   {
   }
}

Thursday, 11 February 2010

Using RichFaces to give versioned URL's

RichFaces has some nice components that can load scripts and styles when placed anywhere in the xhtml page and automatically inserts them into the header at render time. For example a footer could say it requires stylesheet, after you've already defined a header.

The tags are pretty simple there is the style loader, loadStyle:
<afj:loadstyle src="/css/test.css"></afj:loadstyle>
And the script loader, loadScript:
<afj:loadscript src="/javascript/test.js">

The src can be a path be:
  1. Relative to the current page.
  2. Path relative to the context if starting with a slash.
  3. Or loaded by RichFaces resource framework
    Using "resource:///path/to/file"
Now, the problem most web developers always face is that of caching. If you disable caching then the site is slow for the user and your bandwidth is used up. If you enable caching and release a new version, how do you clear the user's and ISP's cache so they can see your changes?

RichFaces already deals with this by creating virtual URL's for all their resources served (the static javascript files and the generated css files and images). It prepends "afj/g/[version]" to the url's. For example:
<script src="/myapp/jsf/a4j/g/3_3_2.SR1jquery.js" type="text/javascript">
Where myapp is the name of the context and I have a mapping to the faces servlet for /jsf/*. This gets prepended to all resources served through the framework, even ones from your war.

This neat little feature can be used to version your own deployments, so that after each build a new URL is generated.

If your using maven its simple.
First add this to your web.xml:
<!-- 
  not used, but needs to be set
-->
<context-param>
  <param-name>org.ajax4jsf.RESOURCE_URI_PREFIX</param-name>
  <param-value>a4j/${project.version}-r${buildNumber}</param-value>
</context-param>
<!-- 
  the prefix used by richfaces for global cached resources
-->
<context-param>
  <param-name>org.ajax4jsf.GLOBAL_RESOURCE_URI_PREFIX</param-name>
  <param-value>a4j/g/${project.version}-r${buildNumber}</param-value>
</context-param>
<!-- 
  the prefix used by richfaces for session cached resources
-->
<context-param>
  <param-name>org.ajax4jsf.SESSION_RESOURCE_URI_PREFIX</param-name>
  <param-value>a4j/s/${project.version}-r${buildNumber}</param-value>
</context-param>
Next you'll need to add the buildnumber plugin:
<build>
   <plugins>
      <plugin>
         <groupId>org.codehaus.mojo</groupId>
         <artifactId>buildnumber-maven-plugin</artifactId>
         <executions>
            <execution>
               <phase>validate</phase>
               <goals>
                  <goal>create</goal>
               </goals>
            </execution>
         </executions>
         <configuration>
            <doCheck>false</doCheck>
            <doUpdate>false</doUpdate>
         </configuration>
      </plugin>
      <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-war-plugin</artifactId>
         <configuration>
            <warSourceDirectory>WebContent</warSourceDirectory>
            <webXml>src/PROD/web.xml</webXml>
            <filteringDeploymentDescriptors>true</filteringDeploymentDescriptors>
            <archive>
               <manifest>
                  <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
               </manifest>
               <manifestEntries>
                  <Implementation-Build>${buildNumber}</Implementation-Build>
               </manifestEntries>
            </archive>
         </configuration>
      </plugin>
If your using eclipse to do development then you'll probably want to have 2 web.xml files. One that eclipse can read with the filtering (property subsititution turned off) and one for production builds.
In

Thursday, 4 February 2010

Safari removed javascript API

In the latest version of Safari (WebKit) they have removed some deprecated javascript API & tags which can cause problems with older code. This is my attempt to keep track of them so I can search through the code and change it to use a newer method.

Here is a list of what works in Firefox 3 or Internet Explorer but not Safari:

  • document.[form name] where [form name] matches the element <form name='[form name]'>
    Replacement:Use document.getElementById() instead
  • document.embeds
    Replacement:
    Use document.getElementById() instead
  • Form element's id defaulted to their name
    Replacement:
    Manually specify the id
  • <applet> tag
    Replacement:
    <object> tag
That's it for the moment but I'll keep updating the list as I find them.

Tuesday, 2 February 2010

Building your own jbossall-client.jar

The newer versions of JBoss (I'm using 5.1.0 GA) very annoyingly have all the client jars split into at least 47 separate jars! Which is a bit rude when you consider the total number of jars they expect you to copy into your tomcat/lib dir is a whopping 93.

JBoss are declined to make the client jars into a single file again as its meant to make it easier to upgrade... That's arguable but still leaves us with all these files to copy around. After searching the net I've found a rather nice solution which I have adapted below.
The way I use it is as follows. I combine all jboss*.jar files into a single jar and then copy the additional dependencies.

Create a directory an empty directory and put the build.xml in it and create a /jboss dir. Copy all jboss jboss*.jar from jboss-5.1.0.GA/client.

Create a /build.xml:
<project default="combine">
   <property name="temp.dir" value="unpacked" />
   <property name="lib.dir" value="jboss" />
   <property name="jar.filename" value="jbossall-client.jar" />

   <target name="clean">
      <delete dir="${temp.dir}" quiet="true" />
      <delete file="${base.dir}/${jar.filename}" quiet="true" />
   </target>
   
   <target name="unjar.jar">
      <unjar dest="${temp.dir}">
         <patternset>
            <exclude name="META-INF/MANIFEST.MF" />
         </patternset>
         <fileset dir="${lib.dir}">
            <include name="**/*.jar" />
         </fileset>
      </unjar>
   </target>


   <target name="combine" depends="unjar.jar">
      <jar jarfile="${basedir}/${jar.filename}"
         basedir="${temp.dir}" update="true"
         compress="false">
      </jar>
   </target>
</project>
It will extract and recombine the jar files for you.

The dependencies I also copy across are:
concurrent.jar
ejb3-persistence.jar
hibernate-annotations.jar
javassist.jar
jmx-client.jar
jmx-invoker-adaptor-client.jar
jnp-client.jar
trove.jar
xmlsec.jar

Monday, 1 February 2010

Automake unable to detect vendor on RedHat EL5

After chasing down some build problems with subversion 1.6.9 on EL5 I was noticing that it was detecting the system as x86_65-unknown-linux-gnu. Worried that it would cause me later problems I started digging through the configure script. It lead me to the build/config.guess script.
After spending too long searching the net and trying to resolve the issue, I just did this:
subversion-1.6.9>cp /usr/lib/rpm/config.guess ./build/config.guess
Basically overriding the one that came with the distro with redhat's hack.

Saturday, 16 January 2010

Using a NavigiationHandler to do HTTP/HTTPS switching

In a project I was working on, we wanted the log in page and anything to do with payment to be secure using HTTPS, whilst the majority of the site would be unsecure using HTTP. JSF 1.2 doesn't really give you anyway of doing this other than manually specifying the full url for every single link and button on the site. This removes a lot of the flexibility and simplicity of JSF. I came up with a pretty good solution. For any link or action that you know will switch between secure and unsecure, which isn't that hard to know (if you design the site with that in mind), then you configure it to be a redirect. Then you create your own navigation handler which applies rules you create yourself, to determine which parts should be secure or not.

This is a very simple solution that when a redirect occurs, it checks the path of the destination page (its viewId). If it starts with "/secure" it gives a full HTTPS url and for all others, it gives a full HTTP url. To activate it, have a navigation rule with a redirect. e.g. a login button with a result "userLogin":
<navigation-case>
 <from-outcome>userLogin</from-outcome>
 <to-view-id>/secure/login.xhtml</to-view-id>
 <redirect/>
</navigation-case>
<navigation-case>
 <from-outcome>home</from-outcome>
 <to-view-id>/index.xhtml</to-view-id>
 <redirect/>
</navigation-case>
Then on successful login return result "home" to redirect back to HTTP.

Next you'll need to create and configure your own navigation handler. The easiest way is to copy the entire contents of the mojarra NavigationHandlerImpl and change one line. In the handleNavigation function the line String newPath = viewHandler.getActionURL(context, caseStruct.viewId); is replaced as shown below:
public void handleNavigation(FacesContext context, String fromAction, String outcome) {

   // ...
   
   if (caseStruct != null) {
      ViewHandler viewHandler = Util.getViewHandler(context);
      assert (null != viewHandler);

      if (caseStruct.navCase.hasRedirect()) {
         // perform a 302 redirect.
         // ---- start change ---
         String newPath = prependSecure(context, viewHandler, caseStruct.viewId);
         // ---- end change -----
         
   //....
}
Download the full source here.

Below is the additional code to support to prependSecure function, that builds up a full url with scheme for the page being redirected to.
private static final String SCHEME_HTTP = "http";
private static final String SCHEME_HTTPS = "https";
private static final int HTTPS_DEFAULT_PORT = 443;
private static final int HTTP_DEFAULT_PORT = 80;
private static final String PREFIX_SECURE = "/secure/";

private String prependSecure(FacesContext context, ViewHandler viewHandler,
      String viewId) {
   boolean isSecure = (viewId != null && (viewId.startsWith(PREFIX_SECURE)));

   String prefix = getServerUrlForceScheme(context, isSecure);

   return prefix + viewHandler.getActionURL(context, viewId);
}

public static String getServerUrlForceScheme(FacesContext context,
      boolean secure) {
   String scheme;
   int defaultPort;
   if (secure) {
      scheme = SCHEME_HTTPS;
      defaultPort = HTTPS_DEFAULT_PORT;
   } else {
      scheme = SCHEME_HTTP;
      defaultPort = HTTP_DEFAULT_PORT;
   }

   HttpServletRequest request = (HttpServletRequest) context
         .getExternalContext().getRequest();

   int port;
   //use current port if known (ideally make this configuration driven)
   if ((!SCHEME_HTTPS.equalsIgnoreCase(request.getScheme()) && !secure)
         || (!SCHEME_HTTP.equalsIgnoreCase(request.getScheme()) && secure))
      port = request.getServerPort();
   else
      port = defaultPort;

   String basePath = scheme + "://" + request.getServerName()
         + (defaultPort == port ? "" : ":" + port);

   return basePath;
}

Tuesday, 12 January 2010

Updating the nextval of all sequences in PostgreSQL

If you've been doing SQL level inserts for testing purposes, then when you go to use the sequences then they will have a nextval that already exists in your table.

So below is a script which updates the nextval for all fields that were created using bigserial/serial:
/* Updates all the sequences to have a next value of max+1 excluding the list passed */
CREATE OR REPLACE FUNCTION fn_fixsequences(excludes text) RETURNS integer AS
$BODY$
DECLARE
themax BIGINT;
mytables RECORD;
num integer;
BEGIN
 num := 0;
 FOR mytables IN 
  select relname, ns.nspname, a.attname, pg_get_serial_sequence(c.relname, a.attname) as seq
  FROM pg_catalog.pg_attribute a INNER JOIN 
   pg_catalog.pg_class c ON c.oid=a.attrelid
    inner join pg_catalog.pg_attrdef d
    on d.adrelid = a.attrelid AND d.adnum = a.attnum AND a.atthasdef
       LEFT JOIN pg_catalog.pg_namespace ns ON ns.oid = c.relnamespace
  WHERE 
    pg_catalog.pg_table_is_visible(c.oid)
   and a.attnum > 0 AND NOT a.attisdropped  and atttypid=20
   and relname not like 'pg_%' and relname not like excludes
   and ns.nspname not in ('information_schema', 'pg_catalog')
   and c.relkind='r'
   and not pg_get_serial_sequence(c.relname, a.attname) is null
 LOOP
      EXECUTE 'SELECT MAX('||mytables.attname||') FROM '||mytables.nspname||'.'||mytables.relname||';' INTO themax;
      IF (themax is null OR themax < 0) THEN
       themax := 0;
      END IF;
      themax := themax +1;
      EXECUTE 'ALTER SEQUENCE ' || mytables.seq || ' RESTART WITH '||themax;
      num := num + 1;
  END LOOP;
 
  RETURN num;
 
END;
$BODY$
LANGUAGE 'plpgsql' VOLATILE;
COMMENT ON FUNCTION fn_fixsequences() IS 'Updates all the sequences to have a next value of max+1 excluding the list passed';

Saturday, 9 January 2010

Getting JBoss Scheduler working

When using the @Service annotation together with the scheduler has given me no end of trouble so I'd like to share my working configuration.

It took me a while to get the objectName right. If your not using @Service(objectName='name') but instead using @Service(name='name') then the final object name is not the same as what they imply at EJB Extensions Help.
Instead I'd recommend going into the JMX console of the running server and searching for it manually, listed under 'jboss.j2ee' for the name you've specified AND type=ManagementInterface.

As well as defining the @Service, you'll also need to define the @Management.

@Management(SampleServiceMBean.class)
@Service(name = SampleServiceMBean.SERVICE_NAME)
public class SampleServiceMBean implements SampleService
{
 public final static String SERVICE_NAME = "SampleServiceMBean";
 
 public void process(ObjectName name, Date date)
 {
  log.info("process {} {}", name, date);
 }

Then create a file the appropriate place
e.g. MyEar.ear/MyEjb.jar/META-INF/myscheduler-service.xml:

<?xml version="1.0" encoding="UTF-8"?>
<server>
 <mbean code="org.jboss.varia.scheduler.Scheduler" name=":service=MyScheduler">
  <attribute name="StartAtStartup">true</attribute>
  <attribute name="SchedulableMBean">jboss.j2ee:ear=MyEar.ear,jar=MyEjb.jar,name=SampleServiceMBean,service=EJB3,type=ManagementInterface</attribute>
  <attribute name="SchedulableMBeanMethod">process( SCHEDULER_NAME, DATE )</attribute>
  <attribute name="InitialStartDate">NOW</attribute>
  <attribute name="SchedulePeriod">60000</attribute>
  <attribute name="InitialRepetitions">10</attribute>
  <attribute name="FixedRate">true</attribute>
    <depends>
      <mbean code="javax.management.timer.Timer" name="jboss:service=Timer"/>
    </depends>
 </mbean>
</server> 
The parameter list threw me off for a while. If you get errors like java.lang.IllegalArgumentException: Unable to find operation processPending(javax.management.ObjectName,java.util.Date), the the key is the argument list (obviously).

Good luck.

Thursday, 7 January 2010

Disabling RichFaces FacesBeanValidator

If you use the rich:ajaxValidator, for example:
<rich:ajaxValidator event="onblur" />
It automatically adds a FacesBeanValidator to the components list of validators. If you don't have javax.validation or Hibernate Validator setup then you'll get the following exception:
2010-01-07 00:29:19,889 [http-8443-1] WARN  org.richfaces.validator.ObjectValidator - Hibernate Validator could not be instantiated, use stub instead
javax.validation.ValidationException: Unable to find a default provider
 at javax.validation.Validation$GenericBootstrapImpl.configure(Validation.java:248)
 at javax.validation.Validation.buildDefaultValidatorFactory(Validation.java:115)
 at org.richfaces.validator.BeanValidator.(BeanValidator.java:34)
 at org.richfaces.validator.ObjectValidator.createInstance(ObjectValidator.java:54)
 at org.richfaces.validator.ObjectValidator.getInstance(ObjectValidator.java:85)
 at org.richfaces.validator.FacesBeanValidator.validate(FacesBeanValidator.java:103)
 at org.richfaces.component.html.HtmlInputText.validateValue(HtmlInputText.java:52)

The way I'd like to be able to do it is:
<managed-bean>
  <description>
   Override RichFaces dependance on hibernate validator and validation api. This stops boot up exceptions (errors once).
  </description>
  <managed-bean-name>org.richfaces.validator.HibernateValidator</managed-bean-name>
  <managed-bean-class>org.richfaces.validator.NullValidator</managed-bean-class>
  <managed-bean-scope>application</managed-bean-scope>
 </managed-bean>
However, unless the bean is accessed by an EL expression like #{applicationScope['org.richfaces.validator.HibernateValidator']}, then it won't be instantiated and added to the application map.

So the only other solution is to set it with code in a ContextListener:
public void contextInitialized(ServletContextEvent event) {
  event.getServletContext().setAttribute(
    org.richfaces.validator.ObjectValidator.VALIDATOR_PARAM, 
    new org.richfaces.validator.NullValidator());
}
A bit of work just to hide an exception... And all this because I was chasing a red herring.