2010-11-22

Getters called multiple times in JSF

In JSF, getters of your beans might be called multiple times per request - it’s a fact. Therefore making a data source call from the getter will have a negative impact on your performance.

Suppose you have a page with a form on it. The user fills and submits the form. There are two scenarios possible - submit success or failure. The expensive call could be acquiring a list of options to choose from on the form.

  1. The user opens the page.
  2. Make just one expensive database call. I.e. don’t make the same call twice.
  3. The user fills and submits the form.
  4. Display the success page, do not make the expensive database call.

  1. The user opens the page.
  2. Make just one expensive database call. I.e. don’t make the same call twice.
  3. The user fills and submits the form.
  4. Validation error occurs. Return to the form page and make the expensive call (to reload the list of options, for instance).

For these two scenarios putting the expensive database call in the constructor or getter will not work. You will either get too many calls or too few.

Solution


And here is the solution.

package me.m1key.sample.singlecall;

import javax.enterprise.context.RequestScoped;
import javax.faces.context.FacesContext;
import javax.inject.Inject;
import javax.inject.Named;
// ...

@Named
@RequestScoped
public class UserBean {

   @Inject
   private FacesContext facesContext;

   public List<String> getUsernames() {
       if (facesContext.getRenderResponse()) {
           System.out.println("Expensive call...");
       }
       List<String> usernames = new ArrayList<String>();
       usernames.add("Michal");
       usernames.add("Ilinka");
       return usernames;
   }

This part does the trick:
if (facesContext.getRenderResponse()) {
   // Expensive stuff
}

Interested in how to inject FacesContext?

You can download sample code (a Maven app) that works on JBoss 6 M5.

2010-11-20

JSF: Inject FacesContext

In JSF, the standard way of accessing FacesContext is through a static call:

FacesContext context =
   FacesContext.getCurrentInstance();

Static calls defy the purpose of OO programming, make unit testing hard, and you have to create a local variable.

Now, with the dawn of JEE6 and dependency injection you would like to do just that:

@Inject
private FacesContext facesContext;

But it doesn’t work. Here is some explanation why.


The solution


The solution is to use Seam Faces. Seam Faces allow you to access FacesContext, ExternalContext and NavigationHandler without having to make static calls.

Here’s how you can add it to your project:

<dependency>
   <groupId>org.jboss.seam.faces</groupId>
   <artifactId>seam-faces</artifactId>
   <version>3.0.0.Alpha3</version>
</dependency>

And it just works! Of course your server must support CDI.


Unit testing


Obviously, the tests require a bit of clumsy boilerplate. If you find it confusing, you can read more about unit testing with Arquillian.

package me.m1key.sample.singlecall;

import static org.junit.Assert.assertEquals;

import java.io.IOException;

import javax.inject.Inject;

import org.jboss.arquillian.api.Deployment;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.shrinkwrap.api.ArchivePaths;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.jboss.shrinkwrap.impl.base.asset.ByteArrayAsset;
import org.junit.Test;
import org.junit.runner.RunWith;

@RunWith(Arquillian.class)
public class UserBeanTest {
   @Deployment
   public static JavaArchive createTestArchive()
           throws IllegalArgumentException, IOException {
       return ShrinkWrap.create("test.jar", JavaArchive.class)
               .addManifestResource(new ByteArrayAsset(new byte[0]),
                       ArchivePaths.create("beans.xml")).addClasses(
                       UserBean.class, MockFacesContext.class);
   }

   @Inject
   private UserBean userBean;

   @Test
   public void thereShouldBeTwoUserNames() {
       assertEquals(2, userBean.getUsernames().size());
   }

}


Sample


You can download sample code (a Maven app) that works on JBoss 6 M5.

2010-11-18

Mercurial server on Windows with Apache

In this tutorial I will show you how to set up a Mercurial 1.7.1 server on Windows using the Apache server, version 2.2.17. I successfully verified this configuration on Windows 7 and Windows XP.

It should be fairly similar for Unix based system, but I haven’t tried that. You can obviously use a different server too.

Thanks goes to my friend Jeroen who helped me out with this.


Installing Mercurial


Obviously, you need Mercurial. It’s up to you how you want to install it, but make sure that the hg command is available from command line. Use the MSI installation and it will do it for you.

I will assume you installed it to C:\Program Files\Mercurial.

Verify that hg is accessible from command line


Installing Apache


The second step is to obtain and install the Apache server. You may want to install it as a service. There is a nice and simple ApacheMonitor application that will put itself in your system tray and you can control your Apache from there.

I will assume you installed it to C:\Program Files\Apache Software Foundation\Apache2.2.


Installing Python


For the Mercurial server to work, you need to have Python installed. The Python version must correspond to the Mercurial version. For Mercurial 1.7.1 you should get Python 2.6.

Please not that you do NOT need to set a system variable called PYTHONPATH as some tutorials suggest.

I will assume you installed your Python to C:\Python26.


Downloading Mercurial source


Download Mercurial source for your Mercurial version and unpack it. If you are lazy, you don’t have to do it - all the necessary files are available on my website (see links below).


Configuring Mercurial


  1. Go to your Mercurial installation directory.
  2. Unpack the library.zip file contents into a folder called lib. You will have to create it yoruself. In this folder the contents of the library.zip file should reside direcly, i.e. the path to for instance zipfile.pyc should be C:\Program Files\Mercurial\lib\zipfile.pyc.
  3. Move the C:\Program Files\Mercurial\templates folder into the new lib folder.


Creating a projects folder


You need to create a project where Mercurial will store your projects. I will assume it’s C:\Projects\hg.


Configuring Apache


In the Apache htdocs folder put this file - hgweb.cgi. Get it from the Mercurial source. In another folder (I will assume it’s the Mercurial installation folder) put hgweb.config - just create it.

Please note those two files are also available for download from my website (see links below).

hgweb.cgi


#!c:/Python26/python.exe -u
#
# An example hgweb CGI script, edit as necessary
# See also http://mercurial.selenic.com/wiki/PublishingRepositories

# Path to repo or hgweb config to serve (see 'hg help hgweb')
config = "C:/Program Files/Mercurial/hgweb.config"

# Uncomment and adjust if Mercurial is not installed system-wide:
import sys; sys.path.insert(0, "C:/Program Files/Mercurial/lib")

# Uncomment to send python tracebacks to the browser if an error occurs:
import cgitb; cgitb.enable()

from mercurial import demandimport; demandimport.enable()
from mercurial.hgweb import hgweb, wsgicgi
application = hgweb(config)
wsgicgi.launch(application)

So if you got this file from the source, you need to make it look more or less like this one.

hgweb.config


[collections]
C:\Projects\hg = C:\Projects\hg
[web]
allow_push = *
style = monoblue
contact = you@localhost.com
push_ssl = false

C:\Projects\hg is, again, where Mercurial will contain your projects. It allows anyone to push. It uses the monoblue style. Provide your email in the contact field. The last line makes SSL not compulsory to be able to push.

httpd.conf


I didn't have to do this, but user Good reported he did and he found it on the editrocket.com site.

Search the httpd.conf file for the line
Options Indexes FollowSymLinks
Add ExecCGI to this line. The line should now look similar to the following (NOTE: there may be more options listed):
Options Indexes FollowSymLinks ExecCGI
Next, search for the following:
#AddHandler cgi-script .cgi
Uncomment this line by removing the # in front of the line, and add a .py to the end of the line. The new line should look like this:
AddHandler cgi-script .cgi .py

Thanks Good!

Creating a sample project


Now, for each project you will have to manually create a repository in the C:\Projects\hg folder.

Go to console, go to this folder and type hg init projectName. This will create the repository.


Verifying


Launch the server and go to http://localhost/hgweb.cgi. You should now see your project listed there under projectName.

You can push and pull to http://localhost/hgweb.cgi/projectName/.

Sample hgweb.cgi (I changed the extension to .txt so that the server wouldn’t treat it as a script)
Sample hgweb.config