Using Selenium to test JSF (myFaces impl) application

I have been looking for a good way to do integration testing of webapplications done in JSF, and decided to give Selenium a try.
First I had a look at the Selenium IDE, it is basically a Firefox “record” tool that remembers the actions you do in the webapp, and then saves these actions as a test-template for you to use later on. But I didn’t want to limit myself to any browser, and I would also like to keep an opening for some continous build processes and frameworks. I therefore decided to go for Selenium Remote Control (Selenium RC), which also allows me to write the tests in java.

I know that Selenium recommends using TestNG instead of JUnit (JUnit should be considered a unit testing tool, not for integration testing) , but since I had some (moderate) experience with JUnit from before I decided to go for this. So I downloaded Selenium RC, and created a new java-project in Eclipse with selenium-java-client-driver.jar, selenium-server.jar and junit.jar in my lib-folder. Both selenium-java-client-driver.jar and junit.jar should be added to the build path, but there’s no need to add selenium-server.jar.

Before you run your tests the selenium server needs to be running, and you can manually start this from command-line by running the command “java -jar selenium-server.jar”. But then, you might want to add some options or specify some properties, and perhaps you don’t want to start messing around with the cmd-window at all. So I created an ant-task for starting and stopping the server. This task I can then set as dependency in another task that runs my tests, but this is of course up to you.

The start task:

	<target name="selenium-start">
		<java jar="${lib.test.dir}/${selenium-jar}" fork="true" spawn="true">
			<arg  line="-port ${selenium.port}" />
			<arg  line="-debug" />
			<arg  line="-log ${build.dir}/selenium.log" /> 		
		</java>
	</target>

The stop task:

	<target name="selenium-stop" description="Stop the Selenium RC server on port ${selenium.port}">
		<get taskname="selenium-shutdown" dest="${build.dir}/selenium.log" src="http://localhost:${selenium.port}/selenium-server/driver/?cmd=shutDown" ignoreerrors="true" />
	</target>

So, what about the testing?

If you’re familiar with JUnit, you know that you need to define the setUp() and tearDown() method. Selenium have created a class SeleneseTestCase that extends TestCase of JUnit, and this class is giving you some selenium-utilities which can be nice to have. I decided to create my tests extending the SeleneseTestCase, but you don’t have to – you can extend TestCase directly. Also, to avoid having to specify the setup() and tearDown() method in all my test-cases I created an abstract class called DefaultTest which I would extend for my test-cases. The DefaultTest has this definition:

public abstract class DefaultTest extends SeleneseTestCase
{
	protected Selenium browser;
	public static final String DEFAULT_TIMEOUT = "20000";
	public static final String BROWSER_FIREFOX = "*firefox";
	public static final String BROWSER_CHROME = "*chrome";
	public static final String BROWSER_IE = "*iexplore";
	
	public void setUp() throws Exception
	{
		browser = new DefaultSelenium("localhost", 6666, BROWSER_CHROME, "http://localhost/Test/");
		browser.start();
	}

	public void tearDown() throws Exception
	{
		browser.stop();
	}
}

To create the “virtual” browser object in Selenium you need to specify the server-host, which port to use, which browser to simulate and so on. I’m doing my tests against a local jboss-server running on port 8080. Note that jboss uses the default port of Selenium (4444), so I specified 6666 for my selenium server to run.

To be able to simulate a user typing something or pressing links, it’s obvious that we in the java-code need some way of locating the different web-elements. In Selenium this is referred to as “locators,” in java you specify them as String objects. You can locate an element in different ways, I mention some of them here:

  • By id: String locator = “id=theIdOfMyComponent”;
  • By name: String locator = “name=theNameOfMyComponent”;
  • By xpath: String locator = “xpath=//table[@id=’myTable’]/tbody/tr/td[2]/span/a”;
  • By link content: String locator = “link=ClickMe”;
  • By table-cell-refernce (TableId.Row.Cell): String locator = “myTable.1.0”;
  • By DOM: String locator = “dom=document.forms[‘myForm’].myElement”;

To start with something simple, we can try a TestCase for logging on to an application. As a convention to follow all the methods in your test-cases should start with “test”, like testLogin():

public class LoginTestCase extends DefaultTest
{
	public final void testLogin()
	{
		String userNameLocator = "name=username";
		String passwordLocator = "name=password";
		String componentLocator = "id=idOfTheComponentIExpectToBeThereAfterSuccessfullyLoggingIn";
		
		browser.open("http://localhost:8080/MyWebbAppContextRoot/");
		browser.type(userNameLocator, "myUsername");
		browser.type(passwordLocator, "myPassword");
		browser.click("id=submitButton");
		browser.waitForPageToLoad(DEFAULT_TIMEOUT);
		assertEquals("The title I expect", browser.getTitle());
		assertTrue(browser.isElementPresent(componentLocator));
		
	}
	
}

You run the test by right-clicking the class (run all tests in the class) or individually on each method -> select Run As -> select JUnitTest. Of course this can also be done from an ant task.

The browser.open() specifies the initial page that should be loaded in the browser. Then you can see that we fill in a value for the username and password, and click a button. Whenever there is a click that requires a page reload, you have to follow that command by a “browser.waitForPageToLoad()”. I set my standard timeout to 20 sec (20000ms), but if the response is sent earlier the browser acts upon it immediately. Then we do an assertion on the title of the page we were sent to, as well as an assertion to check that an expected component on this page actually exist. So, this is a testCase that tests for a successful logon. Another test that could be done is for example to pass invalid username/password, to see that unsuccessful logons are handled correctly.

Testing your webapp like this is integration testing, not unit testing. It is therefore likely that to be able to do your test you need to other actions first, like you follow a chain of commands; you might have to log on, maybe click a link to go on the proper page, or you need to do some other actions before you can do your assertions. Thus, I created some static methods in a utility-class that handles the most common operations, so that I can reuse those from my TestCases. Consider a class like this:

public class InteractionUtil extends DefaultTest
{
	public static void login(Selenium browser)
	{
		String userNameLocator = "name=username";
		String passwordLocator = "name=password";
		
		browser.open("http://localhost:8080/MyWebbAppContextRoot/");
		browser.type(userNameLocator, "myUsername");
		browser.type(passwordLocator, "myPassword");
		browser.click("id=submitButton");
		browser.waitForPageToLoad(DEFAULT_TIMEOUT);
	}
}

Then, my testLogin() method would simply be like this:


	public final void testLogin()
	{
		String componentLocator = "id=idOfTheComponentIExpectToBeThereAfterSuccessfullyLoggingIn";
		InteractionUtil.login(browser);
		assertEquals("The title I expect", browser.getTitle());
		assertTrue(browser.isElementPresent(componentLocator));
	}

And it is very easy to also include this logon-action in other testCases where it is needed. All actions you need to do often can go into such a utility-class.

As a final example I will show how to use the JsCookMenu of tomahawk library from Selenium. I struggled quite a bit with this one before I was able to make the action trigger. Since triggering an element in a dropdown-menu like this can be considered a “generic” action I include it in my utility-class:


	public static void clickJsCookMenu(Selenium browser, String theme, String jsCookId, String itemValue)
	{
		String fieldRowLocator = "xpath=//div[@id='"+jsCookId+"']/div/table/tbody/tr[@class='"+theme+"MenuItem' and td = '"+itemValue+"']";
		String jsCookMenuTdLocator = "xpath=//div[@id='"+jsCookId+"']/table/tbody/tr/td[@class='"+theme+"MainItem']";
		String fieldRowHoverLocator = "xpath=//div[@id='"+jsCookId+"']/div/table/tbody/tr[@class='"+theme+"MenuItemHover']";
		String fieldRowActiveLocator = "xpath=//div[@id='"+jsCookId+"']/div/table/tbody/tr[@class='"+theme+"MenuItemActive']";
		
		browser.mouseOver(jsCookMenuTdLocator);
		browser.waitForCondition("selenium.isVisible(\""+ fieldRowLocator+"\");", DEFAULT_TIMEOUT);
		browser.mouseOver(fieldRowLocator);
		browser.mouseDown(fieldRowHoverLocator);
		browser.mouseUp(fieldRowActiveLocator);
	
		browser.waitForPageToLoad(DEFAULT_TIMEOUT);
	}

To explain a bit, I pass as parameters the browser object, the jsCook theme being used (office, ie and so on), the id I’ve given the jscookMenu component in my jsp and the value/text of the item I want to click. I first do a mouseOver to display the menu, and notice the “waitForCondition” command that follows. This means that the browser should wait for the menu to be visible. Then i figured out I needed to do a mouse-over on the row holding the item. This makes the css class change, so I do mouseDown on the “hovered” row. But the mouseDown-event in the jsCook javascript doesn’t trigger the link, actually a mouseUp does, so finally I do a mouseUp-event on the active row. And of course I then wait for the page to reload.

Then from my test-method I can simply do:


	public void testJsCookMenu()
	{
		InteractionUtil.login(browser);
		UserInteractionUtil.clickJsCookMenu(browser, "ThemeIE", "myJsCookId", "theElementIWantToClick");
		//--- assertions ---
	}

I hope this post will give you a brief understanding on how to use Selenium for testing your webapps!

Mod_gzip – how to make configuration work for Apache 1.3, mod_jk and JBOSS/JSF

I’ve been working a couple of days trying to get mod_gzip to work with apache 1.3, enabling compression for JSF application (.faces). Everything seemed so easy in the configuration described here, but I actually troubled quite a lot with it, and wanted to share what I’ve learned. 🙂 I used this page as a starting point for my digging into mod_gzip. I use mod__jk to forward the request to jboss’ tomcat server.

Firs and most important of all, the configuration I use that make things work with JSF:

LoadModule gzip_module     libexec/mod_gzip.so

<IfModule mod_gzip.c>
mod_gzip_on                   Yes
mod_gzip_can_negotiate        Yes
mod_gzip_static_suffix        .gz
AddEncoding              gzip .gz
mod_gzip_update_static        No
mod_gzip_command_version      '/mod_gzip_status'
mod_gzip_temp_dir             /tmp
mod_gzip_keep_workfiles       No
mod_gzip_minimum_file_size    300
mod_gzip_maximum_file_size    500000
mod_gzip_maximum_inmem_size   60000
mod_gzip_min_http             1000
mod_gzip_handle_methods        GET POST
mod_gzip_dechunk              Yes
mod_gzip_send_vary            On

#Files and items to compress
mod_gzip_item_include         file       \.html$
mod_gzip_item_include         file       \.htm$
mod_gzip_item_include         file       \.js$
mod_gzip_item_include         file       \.js
mod_gzip_item_include         file       \.faces
mod_gzip_item_include         file       \.css$
mod_gzip_item_include         file       \.jsp$
mod_gzip_item_include         file       \.jsp

mod_gzip_item_include         uri        \.jsp$
mod_gzip_item_include         uri        \.jsp

mod_gzip_item_include         handler    \.*
mod_gzip_item_include         handler    \.*$

mod_gzip_item_include         mime       ^text/html$
mod_gzip_item_include         mime       ^text/html
mod_gzip_item_include         mime      ^text/plain
mod_gzip_item_include         mime      ^text/plain$
mod_gzip_item_include         mime       ^text/xml$
mod_gzip_item_include         mime       ^text/css$
mod_gzip_item_include         mime       ^text/javascript$
mod_gzip_item_include         mime       ^text/javascript
mod_gzip_item_include         mime       ^application/x-javascript$
mod_gzip_item_include         mime       ^application/javascript$

CustomLog                     logs/mod_gzip.log common_with_mod_gzip_info2
LogFormat                     "%h %l %u %t \"%V %r\" %<s %{Content-type}o  %b mo
d_gzip: %{mod_gzip_result}n In:%{mod_gzip_input_size}n -< Out:%{mod_gzip_output_
size}n = %{mod_gzip_compression_ratio}n pct." common_with_mod_gzip_info2
</IfModule>

I will just say (cause it is important) that without the two lines including the “handler” patterns, I couldn’t get any compression to work when I was playing with JSF applications. All the logging lines had status “DECLINED:EXCLUDED”.

The first thing to do when you’re supposed to do this would be to enable logging of gzip to understand what is going on. Then based on status-codes you can get a clue to why a file is compressed or not.

Add the following line in your conf:
LogFormat “%h %l %u %t \”%V %r\” %<s %b mod_gzip: %{mod_gzip_result}n In:%{mod_gzip_input_size}n -< Out:%{mod_gzip_output_size}n = %{mod_gzip_compression_ratio}n pct.” common_with_mod_gzip_info2

A problem that troubled me were when I got prints like this:
213.162.235.138 – – [21/Aug/2008:15:13:44 +0200] “localhost.localdomain GET /Url/css/styles.css HTTP/1.1” 200 text/plain 0 mod_gzip: SEND_AS_IS:NO_200 In:0 -< Out:0 = 0 pct.

Apache reports the file as 200, but gzip ends up with status code “SEND_AS_IS:NO_200”. Ok, so we go to check the meaning of the status codes, but that didn’t tell us much. Confusing? It really means that this file is cached in the browser, and will not be compressed because of this. Try ctrl+F5 to clear the cache, then it should compress that file as well.

Also, even if I included the line “mod_gzip_item_include file \.js$” to compress javascript, for all the javascripts it reported “SEND_AS_IS:RESPONSE_CONTENT_TYPE_EXCLUDED”
I troubled with this one, but then I remembered something I read on the internet: “The order of processing during each of both phases is not important, but to trigger the compression of a request’s content this request a) must match at least one include rule in each of both phases and # b) must not match an exclude rule in any of both phases.”

In other words, for some reason it didn’t find an include rule for javascript in both phases. I had included mime type ^text/javascript, so I couldn’t understand this. But I felt like navigating blindly, cause I never saw what mime-type the request was. So I added the following to my log statement: “%{Content-type}o” – which would print out the mime-type as well. Then I saw that for some javascripts the mime-type were application/x-javascript. I included this in the include-list as well, then case was closed 🙂

Hope this post will help others in the same situation, or at least it might give you a clue to what is going on 🙂

Ajax ReRendering – How to reRender part of your page in Myfaces using Richfaces

If you read this post I assume you have a general knowledge about what Ajax is, and what capabilities lies within. As a web-developer, there might be times where you would like to not do a full submit of your page, but you would actually like to just update a small part of the page. An application that achieves this gives the user a more “desktop-application”-like experience, and it will mostly also be a saver when it comes to performance, as you only load a part of the page – as well as the data going to be displayed in it.

For Myfaces, both 1.1 and 1.2 specification, there is a component library named RichFaces. The components there are nice to use for adding ajax-features to your application, and there are also possibilities of adding those capabilities to non-ajax components in myFaces or Tomahawk libraries.

You can download the libraries from here: RichFaces. Depending on the version of Myfaces you use, you need to get different RichFaces version. For MyFaces 1.1 core you need the RichFaces 3.1.X libraries, for Myfaces 1.2 core you can get the 3.2.X libaries. The 3.2.X libraries does NOT support Myfaces version 1.1

Then, to make use of the new library you need to add the following to your web.xml:

    <filter>
        <display-name>RichFaces Filter</display-name>
        <filter-name>richfaces</filter-name>
        <filter-class>org.ajax4jsf.Filter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>richfaces</filter-name>
        <servlet-name>Faces Servlet</servlet-name>
        <dispatcher>REQUEST</dispatcher>
        <dispatcher>FORWARD</dispatcher>
        <dispatcher>INCLUDE</dispatcher>
    </filter-mapping>

On the top of your jsps you need to include the taglibraries by inserting these lines:
<%@ taglib uri=”http://richfaces.org/rich&#8221; prefix=”rich”%>
<%@ taglib uri=”https://ajax4jsf.dev.java.net/ajax&#8221; prefix=”a4j”%>

So, when it comes to the actual reRendering of components or part of your page, there are several different ways of doing this.

In its simplest form, you can use the a4j:support to add ajax capabilities to ordinary myfaces components. In this example we use it on an inputText. You set the event property (javascript events), give the id of the component(s) you want to reRender, and then you go. Everytime a user releases a key, the reRender will be called, and getting the outText component again:

<h:inputText id="myInput" value="#{myBean.myProperty}">
    <a4j:support event="onkeyup" reRender="outText"></a4j:support>
</h:inputText>

<h:outputText id="outText" value="#{myBean.myProperty}"></h:outputText>

If you use componentBindings, and create those components programmatically, you need to do your custom modification of that component in the setter-method of this component to let the reRendering work. Let’s say you have a panelGrid, and you create it java-side and add two outPutTexts to it. If the value of those outputTexts change, and you want to reRender the panelGrid to display this, then the setter of this component is invoked as long as the getter returns null. So you should do like this:

Jsp:

<h:inputText id="myInput" value="#{myBean.myProperty}">
    <a4j:support event="onkeyup" reRender="outPanel"></a4j:support>
</h:inputText>

<t:panelGrid id="outPanel" binding="#{myBean.htmlPanelGrid}"/>

Java:

public HtmlPanelGrid getHtmlPanelGrid()
{
    return htmlPanelGrid;
}

public void setHtmlPanelGrid(HtmlPanelGrid htmlPanelGrid)
{
    //Clean exisiting children
    htmlPanelGrid.getChildren().clear();

    //Add your modifications
    htmlPanelGrid.getChildren().add(createOutputText("out1","#{myBean.myProperty1}"));
    htmlPanelGrid.getChildren().add(createOutputText("out2","#{myBean.myProperty2}"));

    this.htmlPanelGrid = htmlPanelGrid;
}

private UIComponent createOutputText(String id, String valueBinding)
{
    HtmlOutputText out = new HtmlOutputText();
    out.setId(id);
    ValueBinding vb = FacesContext.getCurrentInstance().getApplication().createValueBinding(
    valueBinding);
    out.setValueBinding("value", vb);

    return out;
}

It is also possible to create the a4j:support programmatically, like this:

HtmlAjaxSupport ajaxSupport = new HtmlAjaxSupport();
ajaxSupport.setEvent("onchange");
ajaxSupport.setId("ajaxSupport");
ajaxSupport.setFocus("myForm:theIdOfComponentIWantToFocusOnAfterAjaxSubmit");
ajaxSupport.setReRender("myForm:idOFComponentIWantToReRender"); 

myUiComponent.getChildren().add(ajaxSupport);

There is also a component that creates a javascript-function for you which you can use to pass properties to your bean, execute actions and finally reRender components. It’s called a4j:jsFunction. Let’s say I have a h:commandButton, and when I click it I want to set a value to a property in my bean, I want to run an action, and then display the results. I could have done this with an t:updateActionListener, but then I would have been forced to do a full submit of the whole page. Instead, I can do it like this:


    //Define javascriptFunction with parameter
    <a4j:form>
        <a4j:jsFunction name="setParameterAndRerender" action="#{myBean.action}" reRender="outPanel" >
             <a4j:actionparam name="param1" assignTo="#{myBean.myProperty1}"  />
         </a4j:jsFunction>
    </a4j:form>

    <t:panelGrid id="outPanel" binding="#{myBean.htmlPanelGrid}"/>

    <h:commandButton value="Click me" onclick="setParameterAndRerender('MyValue');return false;"></h:commandButton>

Notice that I set return false in the onclick of the commandButton to avoid it from submitting. I’ll let ajax handle that.

If you create the a4j:support-object progammatically, I’ve experienced that it doesn’t always find the id of the component you have specified to reRender. To solve this, you can use a combination of this one and the javascript function just described:

HtmlAjaxSupport ajaxSupport = new HtmlAjaxSupport();
ajaxSupport.setEvent("onchange");
ajaxSupport.setId("ajaxSupport");
ajaxSupport.setFocus("myForm:theIdOfComponentIWantToFocusOnAfterAjaxSubmit");

//We don't specify reRender id here, but set a javascript function to run when completed
//ajaxSupport.setReRender("myForm:idOFComponentIWantToReRender");
ajaxSupport.setOncomplete("setParameterAndRerender('MyValue');");

myUiComponent.getChildren().add(ajaxSupport);

So, what about the RichFaces library? In all components the ajax capabilities are included, you don’t have to use a4j:support. When using one of the components the submit will always be an ajax submit, so you can just specify which components to reRender. As a short example, instead of using a h:commandButton you can use a4j:commandButton, like this:

    <a4j:commandButton value="Click me" action="#{myBean.action}"  reRender="outPanel"/>
    <t:panelGrid id="outPanel" binding="#{myBean.htmlPanelGrid}"/>

Also, if you have a part of a page that always should be ajaxRendered, and you are tired of of writing Ids to reRender, simply wrap that part of the page inside an a4j:outputPanel and set the ajaxRendered property to true. In my a4j:commandButton, I don’t have to set the reRender property as this panel is now always reRendered by ajax submits:


    <a4j:commandButton value="Click me" action="#{myBean.action}" />

    <a4j:outputPanel ajaxRendered="true">
        <t:panelGrid id="outPanel" binding="#{myBean.htmlPanelGrid}"/>
    </a4j:outputPanel>

I hope this have given you a brief idea of what is possible to do with ajax/richFaces and also how to do it 🙂

I suggest you to check out the livedemo page to see all the different components that RichFaces give you: Live demo

Migration guide – go from Myfaces 1.1.X to Myfaces 1.2.3

If you are currently using Myfaces 1.1.X and want to start to use Myfaces core 1.2.3, there are certain steps you need to do and to be aware of to be able to make your existing 1.1.X-version application run. JSF 1.2 requires java 1.5 or later, JSP 2.1, JSTL 1.2 and a Java Servlet 2.5 implementation. If you use Jboss, this means that you have to go for jboss version 4.2.2 or higher (supports java5).

  1. First of all you need to download the new Myfaces-1.2 Core libraries, you can do it from here: Myfaces Project
  2. If you have been using Myfaces 1.1.X, you have probably also been using javax.servlet.jar and javax.servlet.jsp.jar. But the 1.2.3 version requires JSP 2.1, JSTL 1.2 and Java Servlet 2.5, so you need to update those libraries. The libraries you want to get are jsp-api-2.1.jar, jstl-1.2.jar and servlet-api-2.5.jar. You can get them from here: Jboss repository.
  3. The libraries are now in place. But you will find out that several classes and methods that existed in Myfaces-1.1.5 are now deprecated. For example, ValueBinding is now ValueExpression, and MethodBinding is MethodExpression. I will give you the different way in creating these programmatically, the way it is with 1.1.5, and the way it should be done in 1.2.3. Note that for ValueExpressions it is necessary to pass the “expected” class as parameter – meaning that if your ValueExpression would be used in the “value” property, the class would probably be java.lang.String. If it was to be used in a “rendered” property, the class should be java.lang.Boolean. If you don’t do this properly you would normally get a class cast exception, or at least some strange results: ValueBinding – 1.1.5
    ValueBinding vb = FacesContext.getCurrentInstance().getApplication().createValueBinding(
                        "#{myBean.myProperty}");
    myComponent.setValueBinding("value", vb);
    

    ValueExpression- 1.2.3

    FacesContext context = FacesContext.getCurrentInstance();
    ValueExpression vex = context.getApplication().getExpressionFactory().createValueExpression(context.getELContext(), "#{myBean.myProperty}", String.class);
    myComponent.setValueExpression("value", vex);
    ValueExpression rex = context.getApplication().getExpressionFactory().createValueExpression(context.getELContext(), "#{myBean.myProperty}", Boolean.class);
    myComponent.setValueExpression("rendered", rex);
    

    MethodBinding – 1.1.5

    MethodBinding methodBinding = FacesContext.getCurrentInstance().getApplication().createMethodBinding(
    			"#{myHandler.action}",null);
    myComponent.setAction( methodBinding );
    

    MethodExpression – 1.2.3

    FacesContext context = FacesContext.getCurrentInstance();
    MethodExpression methodEx = context.getApplication().getExpressionFactory().createMethodExpression(context.getELContext(),
    			"#{myHandler.action}", String.class, new Class[] {});
    myComponent.setActionExpression(methodEx );
    

    ActionListener – 1.1.5

    try
            {    Class[] parameterList = { Class.forName("javax.faces.event.ActionEvent") };
                MethodBinding actionListenerBinding = FacesContext.getCurrentInstance().getApplication().createMethodBinding(
                    "#{myHandler.myListener}",parameterList);
                myComponent.setActionListener(actionListenerBinding );
            }
            catch (Exception e)
           {
                log.error("error setting ActionListener");
            }
    

    ActionListener – 1.2.3

    FacesContext context = FacesContext.getCurrentInstance();
    ELContext elContext = context .getELContext();
    MethodExpression actionListenerExpression = context.getApplication().getExpressionFactory().createMethodExpression(
    elContext,"#{myHandler.myActionListenerMethod}",null,new Class[] {ActionEvent.class});
    
     MethodExpressionActionListener methodExpressionActionListener = new MethodExpressionActionListener(actionListenerExpression);
    
  4. Also rememeber that the tld respects the html 4 specification, so old attribiutes like align, alt, background and color are deprecated. You need to update these and set proper css instead.

Good luck!

Refresh parent window from child window – conditional reloading with javascript and AJAX

If you work with popups, there will be situations where you need the parent window to update based on some changes/submitted values in the popup window. I will show a solution that is done for myfaces/jsf, and it’s based on the ‘onload’ event of the popup window. I also use ajax (a4j implementation) to update the parent window to avoid a submit and full screen refresh there.

The basic conceptual thoughts about this are quite easy: When you submit a form – or do other actions in your popup that sends data to the server, you need to update the parent window. It could be that you in the popup for example added an item to a table that are displayed in your parent window. So, when you click your button or link in the popup that fires of an action or submit of some sort, you also trigger a javascript on the onclick event that retrieves a hidden button in your parent window and cliks it. This would cause a submit /refresh on you parent window, and the new data that you added from the popup is diplsayed.

Popup.jsp: The popup form with submit button and onclick event:


<h:form id="myPopupForm">
                <t:commandButton id="mySubmit" action="#{myHandler.myAction}" onclick="updateParentWindow();" value="#{messages.myMessage}" >
                </t:commandButton>
</h:form>

Parent.jsp: The parent window hidden button that submits the form or in other ways (onclick event) refreshes that page

<h:form id="updateParentForm" >
        <t:commandButton style="display: none; visibility: hidden;" id="updateParentButton" action="#{myHandler.myAction}"></t:commandButton>
 </h:form>

The script that finds the parent button and clicks it

function updateParentWindow()
{
    var elementToGet = "updateParentForm"+ ":" + "updateParentButton";

    var form = document.forms['updateParentForm'];

    var button = form.elements[elementToGet];

    button.click();
}

But then, if we do it like this – what are the guarantee that the action we fired off in the popup is finished before the parent view is refreshed? None. If we do a submit in the popup, that page will reload. During reloading there is an onload-event that is called, and the popup is not reloaded until the action we fired off has finished. In other words, putting the function inside the onload-event is safe, it will never refresh the parent window before the submit you did is completed. So, let’s move the call of the updateParentWindow()-function from the button to the onload-event of the popup-page:Popup.jsp

<f:verbatim>
    <body onload="updateParentWindow();">
</f:verbatim>

If you now try your application you should se that if you submit the popup, as soon as it has reloaded, your parent window will refresh as well. Nice! This is one big step in the right direction. But there are still some issues to deal with. If I open my popup-window for the first time, I don’t want my parent window to refresh, cause I haven’t done any changes to the data in my popup yet. Having the updateParentWindow() call in the onload event as it is in the example abowe would cause the parent window to be refreshed also the first time the window opens.We want to avoid this, therefore we add some knowledge that the onload event should only trigger the function if the popup page was reloaded by some clicks/submit in the popup-page itself. What we do is to add a boolean value in javascript somewhere inside the body (not the head) of the parent page. It defaults to false, but the links/buttons in the popup will call a small function that set’s this property to true. Then we do a conditional test in the onload event, and only continue if this value is true.The javascript boolean inside the body of the parent page:Parent.jsp

<f:verbatim>
    <script type="text/javascript">
        var fromPopup = false;
    </script>
</f:verbatim>

Our links and buttons in the popup will on the onclick-event set this property to true:Popup.jsp


<h:commandLink onclick="window.opener.fromPopup = true;" action="#{myHandler.myAction}">
        <h:outputText value="myAction"></h:outputText>
</h:commandLink>

And then the conditional reloading would be like this:


<f:verbatim>
    <body onload="if(window.opener.fromPopup)updateParentWindow(); window.opener.fromLink = false;">
</f:verbatim>

It is important to notice that we have to reset the boolean window.opener.fromLink whenever the popup reloads, but we need to do it AFTER the test.

If you are pleased by this solution, no further steps are necessary! But in my application I display a lot of data at the same time, and what I updated in my popup is only a small part of the view in the parent window. Therefore I only wanted to refresh that part of the page, not do a full refresh/reload of my parent window.To do this I use the a4j:commandButton, which has ajax-capabilities and gives me the possibility of rerendering only a part of the page. If you remember we had a hidden button in our parent window that when clicked was responsible for refreshing that page. We modify it a bit, and create an a4j:commandButton instead, and add the reRender property to update the view:Parent.jsp

<a4j:form id="updateParentForm" >
		<a4j:commandButton style="display: none;" id="updateParentButton"  reRender="myComponentOutsideAForm" ></a4j:commandButton>
</a4j:form>

Notice that the area/component I decide to rerender is given by it’s id. Rerendering a component inside a form will fail in Firefox, you will get an error message in the console saying “f has no properties,” and all cliks on links from this point would not work. So either you would have to go for the ordinary submit/refresh, or you can only update areas of the page that doesn’t contain forms. So, applying this code, if you now trigger your submit in the popup page you will se that when it reloads it updates the parent window, but only the part you specified in the ajax-function we created. Avoiding a full refresh gives a better user experience as well as it saves you from retrieving data from the server more than necessary.

Then, what about form validation? Whether you use application validation (business-logic validation in your action methods), or you use the builtin jsf conversion/validation options, or both, it should be possible to handle this as well. If we click a link or button, but the validation fails, we don’t want to update the parent window. This can be done by adding a boolean on the top of our popup.jsp, as well as using this boolean in our updateParentWindow function. Whenever we do an action from the popup, this boolean must be set. If it is business logic validation, it would be easy to set a property if not all requirements are met. If you use the builtin conversion/validation in jsf, you can use the FacesContext.getMessages() to do this. In the popup.jsp, everytime it loads, we get the boolean from our backing bean and pass it on to the javascript. This would avoid the parentWindow to be updated if validation fails.

First, modify the script we use to update parent to respect a boolean parameter:

function updateParentWindow(validationFailed)
{
    if(validationFailed)
	return;
    else
   {
    var elementToGet = "updateParentForm"+ ":" + "updateParentButton";

    var form = document.forms['updateParentForm'];

    var button = form.elements[elementToGet];

    button.click();
   }
}

In the handler we need a boolean property that we can set depending on whether the validation failed or not. We create a private method that will help us to set this boolean, the important thing is that we call it from all our action methods. So the handler might look something like this:

public class MyHandler
{
    private boolean validationFailed = false;

    //Action
    public void doSomething()
    {
        ...
        Some code/business logic
        ...

        If some business logic/validation is not correct, pass this on to the validation method
            setValidation(true);
        If business logic/validation is correct
            setValidation(false);

    }

    private void setValidation(boolean applicationValidationFailed)
    {
        FacesContext context = FacesContext.getCurrentInstance();
        Iterator<FacesMessage> messages = context.getMessages();
        if(messages.hasNext() || applicationValidationFailed)
            setValidationFailed(true);
        else
            setValidationFailed(false);
    }
}

Then on top of our popup jsp inside <% %> tags we retrieve the handler, and we store the boolean property inside a local variable:

<%

MyHandler myHandler = (MyHandler)FacesContext.getCurrentInstance().getApplication ().
getVariableResolver().resolveVariable(FacesContext.getCurrentInstance(),"myHandler");
boolean validationFailed = myHandler.isValidationFailed();
%>

And then we also modify the onload of this page to pass the boolean to the javascript:


<f:verbatim>
    <body onload="if(window.opener.fromPopup)updateParentWindow(<%= validationFailed %>); window.opener.fromLink = false;">
</f:verbatim>

Done! Your popup-page should also respect the validation now, and not update the parent if it fails.

I hope that this example will be helpful, and if not completely adaptable to your requirements it might lead you into the right path. I end this post by showing the popup.jsp and the parent.jsp in whole, to make it easier for you to see what I’ve tried to explain:Parent.jsp

<f:view  >
    <t:document>
    <t:documentHead>
    ...
    </t:documentHead>

    <f:verbatim>
    <body>
    </f:verbatim>

  <f:verbatim>
    <script type="text/javascript">
    //Used in popup
    var fromLink = false;
    </script>
    </f:verbatim>            

    Some content
    ...

    ...

    <h:panelGrid id="myComponentOutsideAForm">
        THIS AREA/CONTENT WILL BE REFRESHED
    </h:panelGrid>

    <a4j:form id="updateParentForm" >
        <a4j:commandButton style="display: none;" id="updateParentButton"  reRender="myComponentOutsideAForm" ></a4j:commandButton>
   </a4j:form>  

<f:verbatim>
    </body>
</f:verbatim>

</t:document>

</f:view>

Popup.jsp

<f:view  >
    <t:document>
    <t:documentHead>
    ...
    </t:documentHead>
<%

MyHandler myHandler = (MyHandler)FacesContext.getCurrentInstance().getApplication ().
getVariableResolver().resolveVariable(FacesContext.getCurrentInstance(),"myHandler");
boolean validationFailed = myHandler.isValidationFailed();
%>
<f:verbatim>
<body onload="if(window.opener.fromPopup)updateParentWindow(<%= validationFailed %>); window.opener.fromLink = false;">

</f:verbatim>

    Some content
    ....

    ....

   <h:commandLink onclick="window.opener.fromPopup = true;" action="#{myHandler.myAction}">
    <h:outputText value="myAction"></h:outputText>
   </h:commandLink>  

<f:verbatim>
</body>
</f:verbatim>

</t:document>
</f:view>

oamSetHiddenInput is not defined – temporarily solution

If you use myfaces-1.1.5, you might have seen this error in you console. There are not too many sites presenting information about what’s causing this, and I struggled a bit myself trying to find out why my javascript console was flooded with this error message for each submit I did.

The problem appears if you have the org.apache.myfaces.AUTO_SCROLL parameter set to true, and you submit a form with one commandButton only. There is a JIRA-issue on it here, if you want some more specific information: MYFACES-1504. To give an example, this page would produce an error if AUTO_SCROLL is enabled:


<h:form id="myForm">

<t:commandButton id="submitMyFormButton"

value="Submit" action="#{myHandler.myAction}" ></t:commandButton >

</h:form>

There are no fixes to this problem yet (as of 20.01.2008), so until there is a version where this is corrected, simply add a hidden commandLink to the same form, and the problem goes away. You can’t hide it with the rendered attribute if myfaces, but have to hide it with css and styling:

<h:form id="myForm">
			<t:commandButton id="submitMyFormButton"
			value="Submit" action="#{myHandler.myAction}" ></t:commandButton >
			<t:commandLink id="stupidHiddenButtonToAvoidException"
			value="Nothing" style="display: none; visibility: hidden;"></t:commandLink>
</h:form>

Customize Tomahawk scheduler to support grouped entries and lazy loading

I am using myfaces (1.1.5) in my web application, and I was in the need of having some sort of a calendar/scheduler where the user would be able to add multiple records from one category/group/datatype. Imagine that you have multiple collections of some types of data, and each item within each type have date properties that could be presented in a calendar. I checked the scheduler of the Tomahawk library (1.1.6), but this was not supported by default. You could only add “standalone” entries, with no possibility of grouping them together.

Still, I decided to give it a try, and wanted to extend the scheduler to support this functionality. Another thought I had was that I wanted to only retreive the data I needed from the server, in other words: If a datatype have several thousand items, but in the month I’m currently viewing there are really just 18 representations, I would like to only fetch those 18 instances, not the whole collection every time.

In the following example I’ve only done the customization for the “month” mode, as this was the one of importance to me. But it should not be difficult to do this also for the other modes if that is needed.

To start, I decided to create a new class called SchedulerEntryGroup. Basically you can think of this as a group that holds from n to many items that should be entries in our calendar.


public class SchedulerEntryGroup
{
    private String name;
    private String dataTypeName;
    private List<Item> items;
    private String color;
    private boolean visible;
    private String dateProperty;
    private String displayProperty;

   public SchedulerEntryGroup()
    {
        items = new ArrayList<Items>();
    }

    publc SchedulerEntryGroup(String name, String color, boolean visible, String dataTypeName, String dateProperty,String displayProperty)
    {
        setName(name);
        setColor(color);
        setVisible(visible);
        setDateProperty(dateProperty);
        setDisplayProperty(displayProperty);
        setDataTypeName(dataTypeName);

    }

   .........
   SET/GET METHODS
   ......... 

}

As you can see the group holds properties like name (label), dataTypeName (which object-type it contains), the list of items and if it should be visible or not (to enable the possibility of activating/deactivating the displayed entries of a group with a checkbox e.g). It also contains a color property, as this should be common for all the entries of the same group. The dateProperty says which property that contains the dates, and the displayProperty which property should be displayed for the entry in the calendar.In my world the Item object holds a map with objects, and that’s why I pass properties as Strings to get them from the object. It’s basically the key in the properties-map. For reference a “short-version” of my Item-class looks like this:


public class Item
{
    private Map<String,Object> properties;

    public Item()
    {
        properties = new HashMap<String,Object>();
    }

    public Object getProperty(String key)
    {
        return properties.get(key);
    }

}

Ok, I have my entry-group now, but I also made a small change to the Entry class itself, as I wanted to have a color property. I extended the DefaultScheduleEntry and created a class SchedulerEntryExtended, the only difference adding a property color:


public class SchedulerEntryExtended extends DefaultScheduleEntry
{
    private String color;

    public SchedulerEntryExtended()
    {
        super();
    }

    public SchedulerEntryExtended(String description, boolean allDay, Date startTime, Date endTime, String title, String subTitle, String color)
    {
        super();
        super.setDescription(description);
        super.setAllDay(allDay);
        super.setStartTime(startTime);
        super.setEndTime(endTime);
        super.setTitle(title);
        super.setSubtitle(subTitle);
        setColor(color);
    }

    public String getColor()
    {
        return color;
    }
    public void setColor(String color)
    {
        this.color = color;
    }
}

Then I had to change the rendering of entries in month-mode, as I want the group’s color to be set as background-color for each entry. To do this I extended and overrode the DefaultScheduleEntryRenderer and created the class ScheduleEntryRenderer. There are two methods I override, the getColor and the renderCompactContent. The getColor was originally implemented like this:


public String getColor(FacesContext context, HtmlSchedule schedule,
                           ScheduleEntry entry, boolean selected)
    {
        return null;
    }

And the renderCompactContent was implemented like this:


protected void renderCompactContent(FacesContext context, ResponseWriter writer, HtmlSchedule schedule, ScheduleDay day, ScheduleEntry entry, boolean selected) throws IOException
    {
        StringBuffer text = new StringBuffer();
        Date startTime = entry.getStartTime();

        if (day.getDayStart().after(entry.getStartTime()))
        {
            startTime = day.getDayStart();
        }

        Date endTime = entry.getEndTime();

        if (day.getDayEnd().before(entry.getEndTime()))
        {
            endTime = day.getDayEnd();
        }

        if (!entry.isAllDay())
        {
            DateFormat format = DateFormat.getTimeInstance(DateFormat.SHORT);
            text.append(format.format(startTime));
            if (!startTime.equals(endTime)) {
                text.append("-");
                text.append(format.format(endTime));
            }
            text.append(": ");
        }
        text.append(entry.getTitle());

        writer.writeText(text.toString(), null);
    }

My ScheduleEntryRenderer class came out like this:

public class SchedulerEntryRenderer extends DefaultScheduleEntryRenderer
{
     public String getColor(FacesContext context, HtmlSchedule schedule,
             ScheduleEntry entry, boolean selected)
     {

	        SchedulerEntryExtended ent = (SchedulerEntryExtended) entry;
         return ent.getColor();
     }

	    protected void renderCompactContent(FacesContext context, ResponseWriter writer, HtmlSchedule schedule, ScheduleDay day, ScheduleEntry entry, boolean selected) throws IOException
     {

	        StringBuffer text = new StringBuffer();
         Date startTime = entry.getStartTime();

	        if (day.getDayStart().after(entry.getStartTime()))
         {
             startTime = day.getDayStart();
         }

	        Date endTime = entry.getEndTime();

	        if (day.getDayEnd().before(entry.getEndTime()))
         {
             endTime = day.getDayEnd();
         }

	        if (!entry.isAllDay())
         {
         	DateFormat format = DateFormat.getTimeInstance(DateFormat.SHORT);
         	text.append(format.format(startTime));
         	if (!startTime.equals(endTime)) {
         		text.append("-");
         		text.append(format.format(endTime));
         	}
         	text.append(": ");
         }
         text.append(entry.getTitle());

	        StringBuffer entryStyle = new StringBuffer();
 		  entryStyle.append("height: 100%; width: 100%;");
 		  String entryColor = getColor(context, schedule, entry, selected);
 		  if (entryColor != null)
 		  {
 		      entryStyle.append("border-color: ");
 		      entryStyle.append(entryColor);
 		      entryStyle.append(";");
 		      entryStyle.append("background-color: ");
 		      entryStyle.append(entryColor);
 		      entryStyle.append(";");
 		  }
 		  writer.startElement(HTML.DIV_ELEM, null);
 		  writer.writeAttribute(HTML.STYLE_ATTR,entryStyle.toString(), null);
 		  writer.writeText(text.toString(), null);
 		  writer.endElement(HTML.DIV_ELEM);
 	}
}

Notice that in getColor() I cast and use my SchedulerEntryExtended to retrieve the color. What I do in the renderCompactContent method is that I create an entryStyle string, and you can see that I apply the entryColor both to the border and the background. Then I wrap the text inside a div, and apply the entryStyle on this div. Thanks for examples at IRIAN and the tomahawk-examples.zip to point me in the direction of how to do this.

Then the main thing that remains is to have a handler that holds the model for the calendar, as well as having business-logic for using our entry-groups.This is my ScheduleHandler, which have methods for adding, deleting, displaying and hiding groups, as well as the method that creates the model of the calendar, createModel(). The “container” for all the groups that is to be rendered is a linked hashmap named schedulerMap:


public class SchedulerHandler implements Serializable
{
    private static final long serialVersionUID = -8815771399735333108L;

    private ScheduleModel model;
    private Map<String, SchedulerEntryGroup> schedulerMap;
    private SchedulerEntryGroup entryGroupToDelete;
    private SchedulerEntryGroup entryGroupToHide;
    private SchedulerEntryGroup entryGroupToDisplay;
    private Date selectedDate;

    public SchedulerHandler()
    {
        model = new SimpleScheduleModel();
        model.setMode(3);
        model.setSelectedDate(new Date());
        schedulerMap = new LinkedHashMap<String, SchedulerEntryGroup>();
        selectedDate = new Date();
        createModel();
    }

    public Collection<SchedulerEntryGroup> getEntryGroups()
    {
        return schedulerMap.values();
    }

    public void addGroup(SchedulerEntryGroup entryGroup)
    {
        schedulerMap.put(entryGroup.getName(), entryGroup);
        createModel();
    }

    public void hideEntryGroup()
    {
           SchedulerEntryGroup group = schedulerMap.get(getEntryGroupToHide().getName());
        group.setVisible(false);
        schedulerMap.put(group.getName(), group);
        createModel();
    }

    public void displayEntryGroup()
    {
        SchedulerEntryGroup group = schedulerMap.get(getEntryGroupToDisplay().getName());
        group.setVisible(true);
        schedulerMap.put(group.getName(), group);
        createModel();
    }

    public void deleteEntryGroup()
    {
        if(schedulerMap.containsValue(getEntryGroupToDelete()))
        {
            schedulerMap.remove(entryGroupToDelete.getName());
            createModel();
        }
    }

    public ScheduleModel getModel()
    {
        return model;
    }

    public void setModel(ScheduleModel model)
    {
        this.model = model;
    }

   private void createModel()
    {

        setModel(new SimpleScheduleModel());

        int i = 0;
        String id = "";
        for(Entry<String, SchedulerEntryGroup> e : schedulerMap.entrySet())
        {
            id = "Entry_"+e.getValue().getName()+i;
            SchedulerEntryGroup entryGroup = e.getValue();
            if(entryGroup.isVisible())
            {

                List<Item> items = entryGroup.getItems();

                for(Item i : items)
                {
                    SchedulerEntryExtended entry = new SchedulerEntryExtended();
                    entry.setColor(entryGroup.getColor());

                     Object o = i.getProperty(entryGroup.getDateProperty());

                     String s = (String) o;
                     Date d = null;
                     if(StringUtils.isNotBlank(s))
                     {
                        try
                        {
                            d = DateUtil.parseDate((String)o);
                        } catch (ParseException pe)
                        {
                            // TODO Auto-generated catch block
                            pe.printStackTrace();
                        }
                        model.setSelectedDate(d);
                        entry.setId(id);
                        entry.setTitle(i.getProperty(entryGroup.getDisplayProperty()));

                        entry.setStartTime(d);
                        entry.setEndTime(d);
                        entry.setAllDay(true);
                        model.addEntry(entry);
                     }
                     i++;
                }
           }
        }

        if(getSelectedDate() != null)
            model.setSelectedDate(getSelectedDate());
        else
            model.setSelectedDate(new Date());
        model.setMode(3);
        model.refresh();
    }

      public SchedulerEntryGroup getEntryGroupToDelete()
    {
        return entryGroupToDelete;
    }
    public void setEntryGroupToDelete(SchedulerEntryGroup entryGroupToDelete)
    {
        this.entryGroupToDelete = entryGroupToDelete;
    }
    public Date getSelectedDate()
    {
        return selectedDate;
    }
    public void setSelectedDate(Date selectedDate)
    {
        if(selectedDate != null)
        {
            this.selectedDate = selectedDate;
            getModel().setSelectedDate(selectedDate);
            createModel();
        }
    }
    public SchedulerEntryGroup getEntryGroupToHide()
    {
        return entryGroupToHide;
    }
    public void setEntryGroupToHide(SchedulerEntryGroup entryGroupToHide)
    {
        this.entryGroupToHide = entryGroupToHide;
    }
    public SchedulerEntryGroup getEntryGroupToDisplay()
    {
        return entryGroupToDisplay;
    }
    public void setEntryGroupToDisplay(SchedulerEntryGroup entryGroupToDisplay)
    {
        this.entryGroupToDisplay = entryGroupToDisplay;
    }

}

To add a group just create a new instance of it, fill up and set the Item-collection, and pass the group as an argument to the addGroup()-method like this:


public void addEntryGroup()
{
        boolean visible = true;
        SchedulerEntryGroup entryGroup = new SchedulerEntryGroup("myGroup","#CCDD00",visible, "THE_TYPE_OF_DATA", "THE_DATE_PROPERTY", "THE_DISPLAY_PROPERTY");
        SomeService service = new SomeService();
        entryGroup.setItems(someService.getItems("THE_TYPE_OF_DATA", "THE_DATE_PROPERTY"));
        getSchedulerHandler().addGroup(entryGroup);
}

And as you can see the createModel()-method loops the map, and if the group is visible it adds all the items of this group as entries to the calendar. I call createModel() whenever a group is updated or added/deleted, as well as if the selected date of the calendar changes. The “setEntryGroupToDisplay/Hide/Delete()”-methods are used from a jsp/menu to be able to manage the content of the calendar. To hide a group e.g. you could do it very easily with a commandLink and an updateActionListener:


<h:commandLink id="hideLink"  styleClass="myLink" rendered="#{entryGroup.visible}" action="#{schedulerHandler.hideEntryGroup}"  >
          <t:graphicImage value="/images/checkBoxChecked.gif" border="0"></t:graphicImage>
          <t:updateActionListener  value="#{entryGroup}"  property="#{schedulerHandler.entryGroupToHide}"></t:updateActionListener>
</h:commandLink>

Following this example you should now be able to use your calendar with grouped entries. Nice!

But in the beginning of this post I said that I didn’t want to retrieve more items from my backend than I needed to. As the code is now, all the items in the list of the entryGroup are added to the model, and the entryGroup is created before it is aware of which date the calender is currently displaying. This means that it’s impossible to know which items to retreive, as you don’t know which ones are needed.To give the entryGroup knowledge of this, I added a property called dateToFetchFrom, and I move the responsibility of filling up the items-list inside the group-object itself. This is solved by the “fetchItems()”-method. In the calendar-view, if you display a month, it might also contain some days of the previous month as well as the next month. Because of that the date-range I decided to retrieve items for is those three months, the current month, and that month -1 and that month + 1. My SchedulerEntryGroup then became like this:


public class SchedulerEntryGroup
{
    private String name;
    private String dataTypeName;
    private List<Item> items;
    private String color;
    private boolean visible;
    private String dateProperty;
    private String displayProperty;
    private Date dateToFetchFrom;

   public SchedulerEntryGroup()
    {
        dateToFetchFrom = new Date();
	items = new ArrayList<Items>();
    }

    publc SchedulerEntryGroup(String name, String color, boolean visible, String dataTypeName, String dateProperty,String displayProperty)
    {
        setName(name);
        setColor(color);
        setVisible(visible);
        setDateProperty(dateProperty);
        setDisplayProperty(displayProperty);
        setDataTypeName(dataTypeName);

    }

    public void fetchItems()
	{
		Calendar cal = new GregorianCalendar();
		cal.setTime(getDateToFetchFrom());

		cal.add(Calendar.MONTH, -1);
		Date fetchFrom = cal.getTime();
		cal.add(Calendar.MONTH, +1);
		Date fetchTo = cal.getTime();

		List<Item> itemsFetched = new ArrayList<Item>();
		SomeBackendService service = new SomeBackendService();
		service.getItems(getDataTypeName(), getDateProperty(), getDisplayProperty(), fetchFrom, fetchTo );
		setItems(itemsFetched);
	}
   .........
   SET/GET METHODS
   ......... 

}

Of course we then need to modify the createModel() of the SchedulerHandler as well, as we need to take use of the new dateToFetchFrom property as well as the fetchItems()-method. The modified version of createModel() is like this:

private void createModel()
    {
    	setModel(new SimpleScheduleModel());
    	int i = 0;
    	String id = "";
    	for(Entry<String, SchedulerEntryGroup> e : schedulerMap.entrySet())
    	{
    		id = "Entry_"+e.getValue().getName()+i;

    		SchedulerEntryGroup entryGroup = e.getValue();
    		if(entryGroup.isVisible())
    		{

    			entryGroup.setDateToFetchFrom(getSelectedDate());
    			entryGroup.fetchItems();

			List<Item> items = entryGroup.getItems();

                for(Item i : items)
                {
                    SchedulerEntryExtended entry = new SchedulerEntryExtended();
                    entry.setColor(entryGroup.getColor());

                     Object o = i.getProperty(entryGroup.getDateProperty());

                     String s = (String) o;
                     Date d = null;
                     if(StringUtils.isNotBlank(s))
                     {
                        try
                        {
                            d = DateUtil.parseDate((String)o);
                        } catch (ParseException pe)
                        {
                            // TODO Auto-generated catch block
                            pe.printStackTrace();
                        }
                        model.setSelectedDate(d);
                        entry.setId(id);
                        entry.setTitle(i.getProperty(entryGroup.getDisplayProperty()));

                        entry.setStartTime(d);
                        entry.setEndTime(d);
                        entry.setAllDay(true);
                        model.addEntry(entry);
                     }
                     i++;
                }
           }
        }

        if(getSelectedDate() != null)
            model.setSelectedDate(getSelectedDate());
        else
            model.setSelectedDate(new Date());
        model.setMode(3);
        model.refresh();
    }

We see that for each group that is visible, we set the selectedDate of the calendar, and we then execute the fetchItems()-method based on this. If somebody changes the date of the calendar, createModel() is called again, and a new date-range will be the criteria for retrieving items.

As a last comment on the scheduler, I can say that I was troubled by some css-styling and that the borders of the scheduler didn’t render correctly. In my web application I have a style-property on the table tag that says “border-collapse: collapse”. This also effected the rendering of the calendar in Firefox, there were no borders around each day, so the view looked a bit messy. For other reasons I could not remove or change the table tag, so I had to edit the stylesheet of the calendar myself. In the library this can be found under tomahawk-1.1.6.jar\org\apache\myfaces\custom\schedule\resource\css and is named schedule.css. I made the following change to apply separate borders for tables created inside the scheduler only (apply the style to all tables that are children of the div named schedule-compact-default):


div[class=schedule-compact-default]>table
{
border-collapse: separate !important;
}

Ok, I hope that this example have given you some tips on how you can customize the scheduler component. Good luck!