Get the content of an Iframe in Javascript – crossbrowser solution for both IE and Firefox

Ok, let’s imagine the use case: I have an iframe somewhere on my page, and when I click a link or a button I need to get the content of it (could be a textarea e.g.), and then do some stuff with it.

It was easy to do this in IE, but for Firefox I struggled more, as I kept getting the “frame has no properties” error message in the console. And when I solved this I couldn’t get to the content.

There is a lot of references out there claiming that you could use document.frames[‘nameOfMyIframe’] or window.frames[‘nameOfMyIframe’] to get the frame, and then use the .innerHTML to get the content, but both are wrong.

I came up with the following function that seems to do the job in both Firefox (tested on version 2.0.0.11 and 3.03 ) and in IE (6 and 7):


function getContentFromIframe(iFrameName)
{

    var myIFrame = document.getElementById(iFrameName);
    var content = myIFrame.contentWindow.document.body.innerHTML;

    //Do whatever you need with the content    

}

This wasn’t my biggest contribution, but I spent some time trying to find this solution, and for some of you it might be helpful saving you frome some heavy googling.

Because of some postings about people struggling to get this to work, I now include an example using this script. The example consist of two files, main.html and frame.html. If you want to try it locally, put both of them in the same folder and open main.html.

Main.html:

<html>
<head>

</head>
<body>
<script type="text/javascript">
function getContentFromIframe(iFrameName)
{

    var myIFrame = document.getElementById(iFrameName);
    var content = myIFrame.contentWindow.document.body.innerHTML;

    alert('content: ' + content);    

    content = 'The inside of my frame has now changed';
    myIFrame.contentWindow.document.body.innerHTML = content;

}

</script>



</iframe>

<a href="#" onclick="getContentFromIframe('myIframe')">Get the content</a>

</body>

</html>

Frame.html:

<html>
<head>

</head>
<body>

This is inside my frame
</body>

</html>

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!

JBoss guide: How to enable SSL (HTTPS) on JBoss, as well as other “nice-to-know” configurations

From time to time you might have the need of running a web application over https, or there can be requests of having Single-Sign-On between multiple web applications running on your server. This small jboss-guide will give you some clues on how to solve tasks like this, with configurations for both jboss-4.0.4.GA and jboss-4.2.2.GA. Since the name of the server instance might differ and it’s also possible to use custom names, I’ll refer to it as jboss/server/<NAME>/, but what I mean here is for example jboss/server/default/.

Changing the port that jboss runs on

For 4.0.4 you should locate the server.xml inside jboss/server/<NAME>/deploy/jbossweb-tomcat55.sar/, and then change the port=”8080″ parameter in the HTTP Connector to your wishes, for example port 80 as I have done it here.

 <!-- A HTTP/1.1 Connector on port 8080 -->
      <Connector port="80" address="${jboss.bind.address}"
         maxThreads="250" strategy="ms" maxHttpHeaderSize="8192"
         emptySessionPath="true"
         enableLookups="false" redirectPort="8443" acceptCount="100"
         connectionTimeout="20000" disableUploadTimeout="true"/>

For 4.2.2 you do exactly the same, but the server.xml is located inside jboss/server/<NAME>/deploy/jboss-web.deployer/ instead.

Make tomcat able to compile java5 – by default it doesn’t

If you have the need of using java5 (jdk 1.5), you need to set the source-level of the compiler. If you don’t do this and have deployed web-applications with java5 code, you will get exceptions during startup. For 4.0.4 edit the web.xml in jboss/server/<NAME>/deploy/jbossweb-tomcat55.sar/conf. Locate the jsp servlet by searching for <servlet-name>jsp</servlet-name>, and uncomment the section that enables jdk1.5 features:

  <!-- Uncomment to use jdk1.5 features in jsp pages -->
      <init-param>
         <param-name>compilerSourceVM</param-name>
         <param-value>1.5</param-value>
      </init-param>

For 4.2.2 you find the web.xml inside jboss/server/<NAME>/deploy/jbossweb-deployer/conf. Locate the same servlet, and make sure that the parameters both for source & target compiler are set like this:

   	<servlet>
        <servlet-name>jsp</servlet-name>
        <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
        <init-param>
            <param-name>fork</param-name>
            <param-value>false</param-value>
        </init-param>
        <init-param>
            <param-name>xpoweredBy</param-name>
            <param-value>false</param-value>
        </init-param>
        <init-param>
         <param-name>compilerSourceVM</param-name>
         <param-value>1.5</param-value>
        </init-param>
  	<init-param>
         <param-name>compilerTargetVM</param-name>
         <param-value>1.5</param-value>
        </init-param>

Activate support for Single Sign-On

For both versions, locate server.xml (4.0.4 = jboss/server/<NAME>/deploy/jbossweb-tomcat55.sar/, 4.2.2 = jboss/server/<NAME>/deploy/jboss-web.deployer/). Find the “Host” section, and uncomment the following Valve:

   	 <!-- Uncomment to enable single sign-on across web apps
                deployed to this host. Does not provide SSO across a cluster.     

                If this valve is used, do not use the JBoss ClusteredSingleSignOn
                valve shown below. 

                A new configuration attribute is available beginning with
                release 4.0.4:

                cookieDomain  configures the domain to which the SSO cookie
                              will be scoped (i.e. the set of hosts to
                              which the cookie will be presented).  By default
                              the cookie is scoped to "/", meaning the host
                              that presented it.  Set cookieDomain to a
                              wider domain (e.g. "xyz.com") to allow an SSO
                              to span more than one hostname.
             -->

            <Valve className="org.apache.catalina.authenticator.SingleSignOn" />

Then in your jboss-web.xml it’s important that all the web applications that are going to “exchange” credentials points to the same security-domain:

<jboss-web>
 	<security-domain>java:/jaas/USE_THE_SAME_APPLICATION_POLICY_HERE</security-domain>
	<context-root>/YOUR_APPLICATION_ROOT</context-root>
</jboss-web>

If you now open and logon to one application, going to another one running on the same server should not prompt you for username/password again. Note that there are alternatives also if you have applications running on different servers/locations – check the other Valves.

Enable SSL on JBoss

In this example I’m only using a self-signed certificate, but the procedure would be more or less the same even if you are going to use a certificate from a Certification Authority.

  1. Generate the keystore with the following command
    keytool -genkey -alias tomcat -keyalg RSA -keystore NAME_OF_KEYSTORE -validity NUMBER_OF_DAYS
  2. Copy the file into the jboss/server/<NAME>/conf/ directory
  3. Edit the server.xml (4.0.4 = jboss/server/<NAME>/deploy/jbossweb-tomcat55.sar/, 4.2.2 = jboss/server/<NAME>/deploy/jboss-web.deployer/).For 4.0.4 the SSL-connector should be configured like:
     <!-- SSL/TLS Connector configuration using the admin devl guide keystore     -->
          <Connector port="THE_PORT_YOU_LIKE" address="${jboss.bind.address}"
               maxThreads="100" strategy="ms" maxHttpHeaderSize="8192"
               emptySessionPath="true"
               scheme="https" secure="true" clientAuth="false"
               keystoreFile="${jboss.server.home.dir}/conf/THE_KEYSTORE_NAME"
               keystorePass="PASSWORD_FOR_THE_KEYSTORE" sslProtocol = "TLS" />

    For 4.2.2, configure it like this:

     <Connector port="THE_PORT_YOU_LIKE" protocol="HTTP/1.1" SSLEnabled="true"
                   maxThreads="150" scheme="https" secure="true"
                   clientAuth="false"
    	       strategy="ms"
                   address="${jboss.bind.address}"
                   keystoreFile="${jboss.server.home.dir}/conf/THE_KEYSTORE_NAME"
                   keystorePass="PASSWORD_FOR_THE_KEYSTORE"
                   truststoreFile="${jboss.server.home.dir}/conf/THE_KEYSTORE_NAME"
                   truststorePass="PASSWORD_FOR_THE_KEYSTORE"
                   sslProtocol="TLS"/>
  4. Now you should be able to access your application through https. Remember to use https:// instead of http:// in your browser-url, or else it will fail.
  5. Remember that if you want to disable the non-secured port 8080 (or custom), making sure that people can only access through https, comment and disable that connector in the same server.xml.

Tell jboss 4.2.2 to not use the bundled JSF 1.2 implementation

By default this version of jboss comes bundled with the Glassfish JSF 1.2 implementation. If you deploy web applications that use other implementations, like myfaces, you should tell jboss to use the implementations(s) deployed together with the web applications instead. Do this by adding the following to the web.xml of your application(s):

  <context-param>
     <param-name>org.jboss.jbossfaces.WAR_BUNDLES_JSF_IMPL</param-name>
     <param-value>true</param-value>
  </context-param>

Access jboss-4.2.2GA using ip address instead of localhost – use the “-b” parameter

I’ve been using the 4.0.4 version for some time, and I could start it on my machine (accessing it through localhost:8080), and access it from other machines in my network it by using the ip-address of my machine instead of localhost. With 4.2.2, you can start it the same way and it will work from your machine by going localhost. But trying to start the application remotely from other machines by using the ip-address would fail, giving you a 404.

This is because before 4.2.2.GA, jboss was always bound to the any address “0.0.0.0”. But this was considered a security issue, and this default behavior was removed. It’s now up to the user to explicitly configure this.

What you need to do to solve it is to start the jboss with another parameter, you need to set the bind address for the jboss services. The following command would start a jboss server named “myserver” on ip 192.168.100.100:

  run.bat -c myserver -b 192.168.100.100

If you now try to start the application from other machines by using this IP it works! If you use the server named default you can leave out the -c parameter. It’s also possible to revert back to the “old configuration” by using -b 0.0.0.0, but this is not recommended.Ok, I hope that this small guide might be of help to someone! 🙂

Lazy loading of tables in JSF – working with large amount of data

The challenge working with a large amount of data in user-presentation is to serve the user an easy and effective way of getting to the information he/she is looking for. If you have a table with 50.000 rows, you would in some way need to chop the information in smaller pieces, for example having multiple pages displaying 50 rows each, and then having a way of navigating through the pages.

But still, there are some considerations to do this in a way that opts for performance. If the user selects do display page 2, meaning rows 50-100, wouldn’t you like to fetch only those, not all the 50.000 rows? And like in JSF, if you have multiple components (dataTables, dataScrollers) that are dependent on/using the same data, you would like that the data is only retrieved once, not one time for each component. Doing web applications, being able to limit the amount of data that is fetched from the backend is a key to success when it comes to performance and operability issues.

My example is based on Cagatay Civici’s post http://cagataycivici.wordpress.com/2006/07/10/jsf_datatable_with_custom_paging/,
so I will not go through the technical solution and how the “lazy” data model works once more, I suggest you read his post for that. But I give a complete example of how to use his PagedListDataModel, and in addition how to make sure the data is retrieved only once by using phaseListeners.

To keep it simple, let’s imagine that we have a Dictionary in the backend, and a jsp page with static links with letters like ‘A’, ‘B’, ‘C’ and so on throughout the alphabet. Pressing on one of the letters would display all the words that starts with it in the dataTable, and we can use a dataScroller to navigate through multiple pages.

This is my jsp, in which I’ve removed the columns of the dataTable as they are of no importance. Note that the dataTable is set with a valueBinding, and that the value of it is pointing to a dataModel. Also note that we do not render the table if no one has selected a letter. I also just give one link as an example of how to update the property we can refer to as the “selected letter”:

<h:commandLink styleClass="myLink" value="A">
	<t:updateActionListener value="A" property="#{myDataModelHandler.selectedLetter}"></t:updateActionListener>
</h:commandLink>
<h:panelGrid id="mainPanel" width="100%">
	<t:dataTable
	  id="htmlTableView"
	  value="#{myDataModelHandler.activeDataModel}" rendered="#{myDataModelHandler.selectedLetter != null}"
	  binding="#{myHtmlDataTableHandler.htmlDataTable}"
	  headerClass="tableViewHeader"
	  var="row">
	</t:dataTable>
	<t:panelGrid id="footerPager">
		 <t:dataScroller id="ds5"
				for="htmlTableView"
				fastStep="5"
				styleClass="scroller"
				paginator="true"
				paginatorMaxPages="5"
				paginatorTableClass="paginator"
				paginatorActiveColumnStyle="font-weight:bold;"
				rowsCountVar="rowsCount"
				renderFacetsIfSinglePage="true"
			>
			<f:facet name="previous">
				<t:graphicImage id="datascrollerPrevious" value="/images/left-one-step.gif" border="1" styleClass="dataScrollerImage"/>
			</f:facet>
			<f:facet name="next">
				 <t:graphicImage id="datascrollerNext" value="/images/right-one-step.gif" border="1" styleClass="dataScrollerImage"/>
			</f:facet>
			<f:facet name="first">
				<t:graphicImage id="datascrollerFirst"
				value="/images/left.gif" border="1" styleClass="dataScrollerImage"/>
			</f:facet>
			<f:facet name="last">
				 <t:graphicImage id="datascrollerLast"
				 value="/images/right.gif" border="1" styleClass="dataScrollerImage" />
			</f:facet>
		 </t:dataScroller>
	 </t:panelGrid>
</h:panelGrid>

This is the handler (backing bean) that holds the htmlDataTable:

public class MyHtmlDataTableHandler
{
    private HtmlDataTable htmlDataTable;
    public MyHtmlDataTableHandler()
    {

    }

    public HtmlDataTable getHtmlDataTable()
    {
        if(htmlDataTable == null)
        {
            log.debug("htmlDataTable == null");
            HtmlDataTable dataTable = new HtmlDataTable();
            dataTable.setRows(50);
            setHtmlDataTable(dataTable);
        }
        return htmlDataTable;
    }

    public void setHtmlDataTable(HtmlDataTable htmlDataTable)
    {
            this.htmlDataTable = htmlDataTable;
    }
}

You can see that I have modified the getter to create a new instance if the instance it holds is null. It’s important to set the number of rows here, or it will fail when trying to build the lazy data model.The dataModel we use for our dataTable is created and set before the RENDER_RESPONSE phase, as mentioned earlier this is to avoid that the data itself is retrieved multiple times. To be able to do this we need to use a phaseListener, so we implement it in our constructor of the dataModelHandler. Note that we have references here to the dataTableHandler as well as a util for helping us creating a paged data model. As you can see in the phaseListener beforePhase()-method, if no letter has been selected, we don’t create or set the model.

public class MyDataModelHandler
{
	private String selectedLetter;
	private PagedListDataModel activeDataModel;

	private MyHtmlDataTableHandler myHtmlDataTableHandler; //BEAN_REFERENCE SET IN FACES-CONFIG
	private PagedDataModelUtil pagedDataModelUtil; //BEAN_REFERENCE SET IN FACES-CONFIG

	public MyDataModelHandler()
	{
		LifecycleFactory lifecycleFactory = (LifecycleFactory) FactoryFinder.getFactory(FactoryFinder.LIFECYCLE_FACTORY);
		Lifecycle lifecycle = lifecycleFactory.getLifecycle(lifecycleFactory.DEFAULT_LIFECYCLE);

		lifecycle.addPhaseListener
		(
			new PhaseListener()
			{
				public void afterPhase(PhaseEvent arg0)
				{
				}
				public void beforePhase(PhaseEvent arg0)
				{
					if(selectedLetter != null)
					{
						setActiveDataModel(pagedDataModelUtil.createPagedDataModel(selectedLetter, getMyHtmlDataTableHandler().getHtmlDataTable()));
					}
				}	

				public PhaseId getPhaseId()
				{
					return PhaseId.RENDER_RESPONSE;
				}
			}
		);
	}

	public String getSelectedLetter()
	{
		return selectedLetter;
	}

	public void setSelectedLetter(String selectedLetter)
	{
		this.selectedLetter = selectedLetter;
	}

	public PagedListDataModel getActiveDataModel()
	{
		return activeDataModel;
	}

	public void setActiveDataModel(PagedListDataModel activeDataModel)
	{
		this.activeDataModel = activeDataModel;
	}

	public MyHtmlDataTableHandler getMyHtmlDataTableHandler()
	{
		return myHtmlDataTableHandler;
	}

	public void setMyHtmlDataTableHandler(MyHtmlDataTableHandler myHtmlDataTableHandler)
	{
		this.myHtmlDataTableHandler = myHtmlDataTableHandler;
	}

	public PagedDataModelUtil getPagedDataModelUtil()
	{
		return pagedDataModelUtil;
	}

	public void setPagedDataModelUtil(PagedDataModelUtil pagedDataModelUtil)
	{
		this.pagedDataModelUtil = pagedDataModelUtil;
	}
}

The pagedDataModel we create is an instance of PagedListDataModel which is exactly the same as in Cagatay Civici’s post:

public class PagedListDataModel extends DataModel
{
  private int rowIndex = -1;
  private int totalNumRows;
  private int pageSize;
  private List list;

  public PagedListDataModel(List list, int totalNumRows, int pageSize)
  {
    setWrappedData(list);
    this.totalNumRows = totalNumRows;
    this.pageSize = pageSize;
  }

  public boolean isRowAvailable()
  {
    if(list == null)
      return false;
    int rowIndex = getRowIndex();
    if(rowIndex >=0 && rowIndex < list.size())
      return true;
    else
      return false;
  }

  public int getRowCount()
  {
    return totalNumRows;
  }

  public Object getRowData()
  {
    if(list == null)
      return null;
    else if(!isRowAvailable())
      throw new IllegalArgumentException();
    else
	{
      int dataIndex = getRowIndex();
      return list.get(dataIndex);
    }
  }

  public int getRowIndex()
  {
    return (rowIndex % pageSize);
  }

  public void setRowIndex(int rowIndex)
  {
    this.rowIndex = rowIndex;
  }

  public Object getWrappedData()
  {
    return list;
  }

  public void setWrappedData(Object list)
  {
    this.list = (List) list;
  }

}

&#91;/sourcecode&#93;
As you see from the beforePhase()-method in the phaseListener I use a util to create the pagedDataModel. In my application I have a index on the back, but this can of course differ from your situation. I will therefore refer to the backend as "service", and whatever you use that holds and gets the data should not matter.
&#91;sourcecode language='java'&#93;
public class PagedDataModelUtil
{
	public PagedListDataModel createPagedDataModel(String selectedLetter, HtmlDataTable htmlDataTable)
	{
			int startPosition = htmlDataTable.getFirst();
			int dimensionToUse = htmlDataTable.getRows();
			Service service = new Service();
			int totalListSize = service.getTotalNumberOfHits(selectedLetter);

			List<Word> words = new ArrayList<Word>();
			words.addAll(service.getHits(selectedLetter, startPosition, dimensionToUse ));
			PagedListDataModel dataModel = new PagedListDataModel(words, totalListSize, dimensionToUse);
			return dataModel;
	}
}

So, we have now a table that retrieves only the data the user wants to see, and the data is not retrieved multiple times. Fine!But then, what about sorting?? Let’s say that one of the columns we have in our table is length, meaning the length of the word in number of characters. What if I want to sort by this?You could do this as well. I assume you have a header in your table, giving the labels for each column. You can create a commandLink on each label, and if you press one of them you set that value into a property, for example “sortedBy” in the dataModelHandler.First, the link:

 			<t:column>
				<f:facet name="header">
					<t:commandLink id="sortBySomethingLink" value="myColumnLink">
						<t:updateActionListener property="#{myDataModelHandler.sortedBy}" value="theFieldNameIWantToSortOn"></t:updateActionListener>

					</t:commandLink>
				</f:facet>
				<h:outputText value="#{someObject.someValue}" />

			</t:column>

We see that this link updates the property sortedBy in the MyDataModelHandler with the value specified. In the phaseListener, when we call the createPagedDataModel, we now need to pass this property.In other words we modify the createPagedDataModel() method to add support for sorting. We have a new parameter called sortedBy, and use StringUtils to test whether we should bring this one into play or not. If it is null or “” (blank), we don’t sort. We could also keep the sortedBy parameter as a property in our PagedDataModelUtil, in this way we can have an ascending / descending functionality. If the sortedBy parameters equals the sortedBy property, we flip the ascending boolean, if not we sort ascending by default:


private String sortedByLast = "";
private boolean ascending = true;

public PagedListDataModel createPagedDataModel(String selectedLetter, HtmlDataTable htmlDataTable, String sortedBy)
{
			int startPosition = htmlDataTable.getFirst();
			int dimensionToUse = htmlDataTable.getRows();
			int totalListSize = service.getTotalNumberOfHits(selectedLetter);

			List<Word> words = new ArrayList<Word>();
			if(StringUtils.isNotBlank(sortedBy))
			{
				if(sortedBy.equalsIgnoreCase(sortedByLast))
					ascending = !ascending;
				else
					ascending = true;

				words.addAll(service.getHitsSorted(selectedLetter, startPosition, dimensionToUse, sortedBy, ascending));
			}
			else
				words.addAll(service.getHits(selectedLetter, startPosition, dimensionToUse ));

			PagedListDataModel dataModel = new PagedListDataModel(words, totalListSize, dimensionToUse);
			return dataModel;
}

Of course, this puts the responsibility of sorting on the backend service. Somewhere we need to do the sorting. 🙂 If you use a database and have a high number of rows/items, this might be a performance issue. In my application I use Lucene (index), and even with a very high number of rows I don’t have any problems with performance. But doing the sorting in the backend and getting the first 25 items e.g. is anyway faster than retrieving all the items and then do sorting.Ok, that was all this time!

How to do proper URL Encoding in Javascript when using window.open()

In some cases you might want to do encoding of URL-parameters directly inside javascript, simply because you think it’s best to do it there or because you have no other options of getting the parameters encoded. But as I discovered when trying to do this myself, doing this in javascript is not like a walk in the park, or at least there are some important “black holes” you should be aware of.

In my example I use JSF, and I have a h:dataTable component which I build up programmatically in java. For each row I have a link that will open a new popup window that points to another page (jsp) passing some parameters. I needed to do javascript-encoding of parameters, because when I build my table I use expression language (EL – #{} ) and valueBindings (for those of you that are familiar with it). If you use expressions you don’t hold the value itself, but the expression will be “decoded” run-time when the page is rendered.

To show an example, I have a Java-class that has a hashmap called properties, an ordinary key-value thing. By using a get-method that returns the whole map it’s possible through EL to give an “input-parameter” on which property to get. My get-method:

public Map<String, Object> getProperties()
{
	return properties;
}

this would be the expression to get the url:

String url = "#{row.properties['"+SOME_KEY_THAT_POINTS_TO_OBJECT_WITH_URL+"']}";

And then I would create my link in java with value-binding like this:

HtmlOutputLink htmlOutputLink = new HtmlOutputLink();
ValueBinding vb = FacesContext.getCurrentInstance().getApplication().createValueBinding(
"popupWindow('"+ url +"','MY_POPUP_WINDOW', WIDTH,HEIGHT); return false;");
htmlOutputLink.setValueBinding("onclick",vb);

The “normal” way of doing encoding java-side would be something like this:

public String urlEncode(String urlToEncode)
{
	String encodedUrl = "";
	if(urlToEncode != null)
	{
		try
		{
 			encodedUrl = URLEncoder.encode(urlToEncode,"UTF-8");
		}
		catch (Exception e)
 		{
			log.error("Encode exception when encoding url: " + urlToEncode,e);
 			return urlToEncode; //Return unencodedUrl
		}

		return encodedUrl;
	}
}

But in my case, if I were to encode the url before passing it to the javascript, what I would encode would actually be my expression, not the value of it. The result of this encoding would be something like;

 %23%7Brow.properties%5B%27SOME_KEY_THAT_POINTS_TO_OBJECT_WITH_URL%27%5D%7D.

Thus, I was in the need of doing the encoding after the page has been rendered, and then I needed to do it inside the javascript.

I tried two different approaches for encoding the url, the first one which I expected would work, and the second one which I had to do to make it work. What you need to be aware of is that default encoding in javascript is the UTF-8 format, so if you try to decode “on the other side” you have to use UTF-8 in your decoder. I’ve also seen some examples around saying that the escape()-function does some sort of encoding, but this one just escapes (replaces) special characters, for example it converts white-spaces to %20%. Trying to use escape() on the URL and then decoding it with an URL-decoder would fail! Still, I found the need of using the escape()-function as well, and will explain this as I go.

The first function I tried uses the encodeURI()-function inside javascript directly:

function encodeUrl(url)
{
	return encodeURI(url);
}

And from my popupWindow()-function I call the encodeUrl() like this:

var newWindow = '';
function popupWindow(url, name, width, height)
{
	name = name.replace(/\/|\-|\./gi, "");
	var whitespace = new RegExp("\\s","g");
	name = name.replace(whitespace,"");
	if (!newWindow.closed && newWindow.location)
	{
		newWindow.location.href = encodeUrl(url);
	}
	else
	{
		newWindow = window.open(encodeUrl(url),name, "location=no, scrollbars=yes, resizable=yes, toolbar=no, menubar=no, width=" + width + ", height=" + height );
		if (!newWindow.opener)
			newWindow.opener = self;
	}
	if (window.focus)
	{
		newWindow.focus()
	}
}

But doing it like this the decoding seemed to fail, as I couldn’t get the proper value from the url on the receiving side (decodedValue != encodedValue). Having struggled with this one for some time, I discovered that the window.open()-function actually tries to do some sort of decoding of the url itself before it passes it on. So I created another method that encodes on parameter level, and which also uses the escape-method to “protect” the encoded parameters from the window.open()-function.

function encodeUrl(url)
{
 	if (url.indexOf("?")>0)
 	{
		encodedParams = "?";
 		parts = url.split("?");
 		params = parts[1].split("&");
 		for(i = 0; i < params.length; i++)
 		{
			if (i > 0)
	 		{
				encodedParams += "&";
			}
			if (params[i].indexOf("=")>0) //Avoid null values
			{
				p = params[i].split("=");
				encodedParams += (p[0] + "=" + escape(encodeURI(p[1])));
			}
			else
			{
				encodedParams += params[i];
			}
		}
		url = parts[0] + encodedParams;
	}
	return url;
}

One thing to mention in this function is that is uses the encodeURI()-function. This one doesn’t do anything with the reserved characters like ; , / ? : @ & = + $. If you for some reason need to encode these as well, you should use the encodeURIComponent()-function.I tried to do this function without the use of escape(), but then the parameters “arrived” wrongly encoded, and became “corrupted” after I decoded them. Wrapping the encodeURI() inside an escape() solved this problem:

encodedParams += (p[0] + "=" + escape(encodeURI(p[1])));

Finally, inside my receiving page, I managed to decode the parameters and get the values I actually submitted with a java decoding method like this:

public String urlDecode(String urlToDecode)
{
	String decodedUrl = "";
 	if(urlToDecode != null)
 	{
 		try
 		{
 			decodedUrl = URLDecoder.decode(urlToDecode,"UTF-8");
 		}
 		catch (Exception e)
 		{
 			log.error("INVALID URL: " + urlToDecode,e);
 			return "";
 		}
 	}
	return decodedUrl;
}

This way of doing it also works for special characters belonging to the ISO-8859-1 encoding, even if the encoding used is actually UTF-8. As a small reference I can list the proper UTF-8 encoding for the Scandinavian specific characters if you use those, in this way you should be able to check whether your URL is encoded correctly or not if you print it out before decoding it.

  • æ = %E6
  • ø = %F8
  • å = %E5
  • Æ = %C6
  • Ø = %D8
  • Å = %C5

You can also check out this page for a complete reference of encoded characters: http://www.w3schools.com/tags/ref_urlencode.asp

If you want to try an example of how encodeURI and encodeURIComponent works, copy and paste the following javascript and test with your own strings:


<script type="text/javascript">

                var unencodedText = "This is my text that contains whitespaces and characters like # and Ø";
                var encodedText = "";
                var decodedText = "";
                alert('unencodedText: ' + unencodedText);

                //To encode whitespaces and the 'Ø' character - use encodeURI
                encodedText = encodeURI(unencodedText);
                //We see that whitespaces and 'Ø' are encoded, but the '#' is still there:
                alert('encodedText: ' + encodedText);

                //If we decode it we should get our unencodedText back
                decodedText = decodeURI(encodedText);
                alert('decodedText: ' + decodedText);

                //To also encode the '#' we use the encodeURIComponent
                encodedText = encodeURIComponent(unencodedText);
                //Now all the characters have been encoded:
                alert('encodedText: ' + encodedText);

                //To get our unencodedText back we now need to use the decodeURIComponent
                decodedText = decodeURIComponent(encodedText);
                alert('decodedText: ' + decodedText);

            </script>

I hope that you might find this post useful, and that it might save you from some pain in the a** if you try to use the encoding capabilities of Javascript.

Using JSON with JSF

This post will describe what to do if you want to use JSON in your web applications together with JSF. JSON (JavaScript Object Notation) is a way of doing information interchange between your programming language and javascript. In other words you can use it to access for example java objects in the world of javascript, and this will give you the possibility of creating more advanced web applications. Some basic knowledge of javascript is recommended.

  1. First thing to do is to download the JSON RPC library from JsonRpc and put it in your WEB-INF/lib directory (or the directory you use that holds the libs and is packaged together with the war-file). Also remember to add the library to the classpath of your project in Eclipse or other software frameworks you use to develop.
  2. Then secondly you need to include the jsonrpc.js javascript in the header of your page. The script can be found within the downloaded lib inside \json-rpc-java-1.0.1\webapps\jsonrpc. You include it by writing something similar like this in your header:
    <script type="text/javascript" src="${pageContext.request.contextPath}/javascript/jsonrpc.js"></script>
  3. Then you need to add the servlet configuration to the web.xml
    <servlet>
    
    	<servlet-name>com.metaparadigm.jsonrpc.JSONRPCServlet</servlet-name>
    
    	<servlet-class>com.metaparadigm.jsonrpc.JSONRPCServlet</servlet-class>
    
    </servlet>
    
    <servlet-mapping>
    
    	<servlet-name>com.metaparadigm.jsonrpc.JSONRPCServlet</servlet-name>
    
    	<url-pattern>/JSON-RPC</url-pattern>
    
    </servlet-mapping>
  4. Then, on top of your page (outside the <f:view> tags) you write:
    <jsp:useBean id="JSONRPCBridge" scope="session"class="com.metaparadigm.jsonrpc.JSONRPCBridge" />
  5. Then you must register the handler/backing bean you want the json to access. Do this inside the <f:view> tags but within jsp code tags
    </pre>
    <pre><%
    
    MyHandler myHandler = (MyHandler )FacesContext.getCurrentInstance().getApplication ()
    
    .getVariableResolver().resolveVariable(FacesContext.getCurrentInstance(),"myHandler ");
    
    JSONRPCBridge.registerObject("myHandler ", myHandler );%>
  6. From javascript side, you can now access you handler, set properties and execute methods like this:
    function JsonTest()
    
    {
    
    	try
    
    	{
    
    		//The input-parameter to the JSONRpcClient is basically the path to
    		//the servlet you registered in web.xml
    
    		jsonrpc = new JSONRpcClient("/YOUR_APPLICATION_CONTEXT_ROOT/JSON-RPC");
    
    		// Call a Java method
    
    		var result = jsonrpc.myHandler.getTestValue();
    
    		//Do something with result.....
    
    		jsonrpc.myHandler.setTestData("SET A PROPERTY TO SOMETHING");
    
    		jsonrpc.myHandler.execute(); //Execute a method
    	}
    
    	catch(e)
    
    	{
    
    		alert(e);
    
    	}
    
    }

Good luck!

Automatic logon to a web application (JSF on jboss-4.2.2.GA) using JCIFS NTLM HTTP Authentication

The goal was to be able to make users which are logged in on a windows domain to automatically authenticate with their windows credentials when starting a web application, and also to make this solution possible both for IE and Firefox. In other words, the user doesn’t have to enter user-name/password when starting a corporate web application.

The web-app is done in JSF (myfaces 1.1.5), the application server is jboss-4.2.2.GA and the library I use to “grab” the windows user is JCIFS NTLM HTTP Authentication version 1.2.17 (http://jcifs.samba.org/). I will try to give a step-by-step explanation to what I did to make this work, and I will point out the differences you need to do compared to JAAS authentication mechanism (which is commonly used with applications running on jboss).

  1. The first ting you need to do is to download and put the jcifs library (jcifs-1.2.17.jar – or newer version) into your JBOSS_HOME/server/<NAME-OF-SERVER>/lib -folder.
  2. In your web.xml, you need to apply the following:
    <filter>
    
    	<filter-name>NtlmHttpFilter</filter-name>
    
    	<filter-class>jcifs.http.NtlmHttpFilter</filter-class>
    
    	<init-param>
    
    		<param-name>jcifs.http.domainController</param-name>
    
    		<param-value>IP ADDRESS_OF_THE_DOMAIN_CONTROLLER</param-value>
    
    	</init-param>
    
    	<init-param>
    
    		<param-name>jcifs.smb.client.domain</param-name>
    
    		<param-value>NAME_OF_DOMAIN</param-value>
    
    	</init-param>
    
    	<init-param>
    
    		<param-name>jcifs.smb.client.username</param-name>
    
    		<param-value>A_USERNAME_IN_ACTIVE_DIRECTORY</param-value>
    
    	</init-param>
    
    	<init-param>
    
    		<param-name>jcifs.smb.client.password</param-name>
    
    		<param-value>PASSWORD_FOR_THIS_USER</param-value>
    
    	</init-param>
    
    	<init-param>
    
    		<param-name>jcifs.util.loglevel</param-name>
    
    		<param-value>3</param-value>
    
    	</init-param>
    
    </filter>
    
    <filter-mapping>
    
    	<filter-name>NtlmHttpFilter</filter-name>
    
    	<url-pattern>/*</url-pattern>
    
    </filter-mapping>

    For the jcifs.http.domainController you specify the ip-address of the domainController you are authenticating against, e.g. 192.168.1.100.

    For jcifs.smb.client.domain you specify the domain that the users are logging in to. If you are uncertain about this check the logon-box in windows (XP login to domain) and write the domain name as its written here.

    In some examples on the net the jcifs.smb.client.username and jcifs.smb.client.password are not included. These are used for pre-authentication, think of it as a user that has access to the active directory so that it can retrieve information about the other users trying to log in. My experience is that if I leave out these two parameters, it works fine for the first user logging in, but for the first user only. When the second user logs in you get a 0xC0000022: jcifs.smb.SmbAuthException in the server log, and the negotiation of user-name/password fails. Including those parameters solves this issue. You could perhaps create a “dummy user” in the Active Directory just for this purpose (if you have administrator rights), if not use any other user of the AD.

    At least when trying to get this up and running I would recommend a logging-level set to 3. And of course you would set the url-pattern of the filter according to the path of the resources you want to protect, e.g. <url-pattern>/myJsps/*</url-pattern>

  3. If you are used to restricting access to your web application, you might have something like this in your web.xml:
    <security-constraint>
    
    	<web-resource-collection>
    
    		<web-resource-name>PROTECTED AREA</web-resource-name>
    
    		<description>Require users to authenticate</description>
    
    		<url-pattern>/*</url-pattern>
    
    	</web-resource-collection>
    
    	<auth-constraint>
    
    		<role-name>SOME_ROLE</role-name>
    
    	</auth-constraint>
    
    </security-constraint>
    
    <login-config>
    
    	<auth-method>BASIC</auth-method>
    
    	<realm-name>MY_NAME</realm-name>
    
    </login-config>
    
    <security-role>
    
    	<role-name>SOME_ROLE</role-name>
    
    </security-role>

    And then you would also have a security-domain set up in jboss-web.xml that points to an application-policy in login-config.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    
    <!DOCTYPE jboss-web PUBLIC "-//JBoss//DTD Web Application 2.3//EN"
    
    "http://www.jboss.org/j2ee/dtd/jboss-web_3_2.dtd">
    
    <jboss-web>
    
    	<security-domain>java:/jaas/MY_APPLICATION_POLICY</security-domain> -->
    
    	<context-root>/MY_CONTEXT_ROOT</context-root>
    
    </jboss-web>

    To be able to use the security filter with success, you would need to remove all of the settings related to security and JAAS. Remove the <security-constraint> (including children) , <login-config> and <security-role> tags from web.xml, and also delete the line specifying the <security-domain> in jboss-web.xml

  4. All the settings server-side are done now, what remains is a very simple configuration of the client/web browser. You need to tell your browser that the host/site you are trying to reach should be considered a trusted site. If you don’t do this, the browser would pop up the usual login-dialog and ask for username and password.
    • Enable it in IE:Go to tools->Internet Options->SecuritySelect Local Intranet icon and press “Sites”Press “Advanced”Fill in the url to the host of your application and press add. (Do not use the full application path or port settings, only the host address – for example: http://192.168.1.100)
    • Enable it in Firefox:Type about:config in the url-barLocate the key network.automatic-ntlm-auth.trusted-urisAdd the url to the host in the value-field. If there is other urls specified, separate them with a comma.
  5. If you now try to start your application, you should be automatically logged in with your windows user. The server log should say something like this if you have a successful authentication:
    [STDOUT] NtlmHttpFilter: THE_DOMAIN\user_name successfully authenticated
    
    against 0.0.0.0<00>/IP_OF_DOMAIN_CONTROLLER

I hope this overview can be helpful, but of course there are situations and special cases that I haven’t covered here. I would have to point you to the documentation and the FAQ on the homepage of the JCIFS library (see link in the start of this post). Still, I spent some time sorting out configurations, parameters and mistakes, so hopefully this will give others a shorter path to the goal.

Good luck!