Use Tomcat Connector for Load Balancing Glassfish or SJSAS

In this tutorial i describe how you can use the Tomcat Connector (mod_jk module) as a load balancer for the Glassfish or Sun Java System Application Server (SJSAS). It’s availabe as a loadable module for the Apache webserver and for the Microsoft IIS.

The Tomcat Connector uses the packet oriented binary AJP13 protocol for the communication between the servlet container and the webserver. This architecture is optimized for speed and is much faster as a proxy configuration.

Download components

  1. Download the actual mod_jk distribution for either Apache or IIS webserver.
  2. Download the actual Tomcat 5.5 distribution.
  3. Download commons-logging and commons-modeler from the Jakarta Project.

Installation for Apache Webserver

Copy the mod_jk distribution files to your apache libexec directory, e.g. /srv/apache/libexec under Linux OS.

Edit the server configuration file httpd.conf and add the following configuration options:

# Load mod_jk module
LoadModule jk_module libexec/mod_jk.so
AddModule mod_jk.c

<IfModule mod_jk.c>
  # Tells the module the location of the workers.properties file
  JkWorkersFile /srv/apache/conf/workers.properties

  # Specifies the location for this module's specific log file
  JkLogFile /var/log/mod_jk.log

  # Sets the module's log level to info
  JkLogLevel info

  # Sets the module's log time stamp format
  JkLogStampFormat "[%a %b %d %H:%M:%S %Y] "

  # JkOptions for SSL
  JkOptions +ForwardKeySize +ForwardURICompat -ForwardDirectories

  # Set mount points for load balancer
  JkMount /app1   loadbalancer1
  JkMount /app1/* loadbalancer1
  JkMount /app2   loadbalancer1
  JkMount /app2/* loadbalancer1
</IfModule>

Installation for Microsoft IIS

  1. In the registry, create a new registry key named “HKEY_LOCAL_MACHINE\SOFTWARE\Apache Software Foundation\Jakarta Isapi Redirector\1.0”
  2. Add a string value with the name extension_uri and a value of /jakarta/isapi_redirect.dll.
  3. Add a string value with the name log_file and a value pointing to where you want your log file to be (for example c:\mod_jk\isapi.log).
  4. Add a string value with the name log_level and a value for your log level (can be debug, info, error or emerg).
  5. Add a string value with the name worker_file and a value which is the full path to your workers.properties file (for example c:\mod_jk\workers.properties)
  6. Add a string value with the name worker_mount_file and a value which is the full path to your uriworkermap.properties file (for example c:\mod_jk\uriworkermap.properties)
  7. Using the IIS management console, add a new virtual directory to your IIS/PWS web site. The name of the virtual directory must be jakarta. Its physical path should be the directory where you placed isapi_redirect.dll (in our example it is c:\mod_jk). While creating this new virtual directory assign it with execute access.
  8. Using the IIS management console, add isapi_redirect.dll as a filter in your IIS/PWS web site. The name of the filter should reflect its task (I use the name glassfish), its executable must be our c:\mod_jk\isapi_redirect.dll. For PWS, you’ll need to use regedit and add/edit the “Filter DLLs” key under HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\W3SVC\Parameters. This key contains a “,” separated list of dlls (full paths) – you need to insert the full path to isapi_redirect.dll.
  9. Restart IIS (stop + start the IIS service), make sure that the tomcat filter is marked with a green up-pointing arrow. Under Win98 you may need to cd WINDOWS\SYSTEM\inetsrv and type PWS /stop ( the DLL and log files are locked – even if you click the stop button, PWS will still keep the DLLs in memory. ). Type pws to start it again.

Setup uriworkermap.properties (IIS only)

In the uriworkermap.properties file you define the mappings for URL’s which should be connected to your load balancer:

# Mapping for app1
/app1=loadbalancer1
/app1/*=loadbalancer1

# Mapping for app2
/app2=loadbalancer1
/app2/*=loadbalancer1

Configuration of Tomcat Connector

Edit the file workers.properties which you have specified in your webserver configuration file:

# Define all worker nodes
worker.list=loadbalancer1

# Set properties for server1 (ajp13)
worker.server1.type=ajp13
worker.server1.host=server1
worker.server1.port=8009
worker.server1.lbfactor=50
worker.server1.cachesize=10
worker.server1.cache_timeout=600
worker.server1.socket_keepalive=1
worker.server1.socket_timeout=300

# Set properties for server2 (ajp13)
worker.server2.type=ajp13
worker.server2.host=server2
worker.server2.port=8009
worker.server2.lbfactor=50
worker.server2.cachesize=10
worker.server2.cache_timeout=600
worker.server2.socket_keepalive=1
worker.server2.socket_timeout=300

# Set properties for loadbalancer1
worker.loadbalancer1.type=lb
worker.loadbalancer1.balance_workers=server1,server2
worker.loadbalancer1.sticky_session=true
worker.loadbalancer1.sticky_session_force=false
worker.loadbalancer1.method=request
worker.loadbalancer1.lock=optimistic
worker.loadbalancer1.retries=3

Restart your Apache webserver.

Installing AJP13 connector into application server

To install the AJP13 connector into your application server you must copy the file

$TOMCAT_HOME/server/lib/tomcat_ajp.jar

from the Tomcat server distribution to your application server’s lib directory. It seems that the actual Tomcat 5.5.25 tomcat_ajp.jar doesn’t work and throws a java.lang.NoSuchMethodError exception. So use the library bundled with Tomcat 5.5.16 and all works ok. You also need to copy the downloaded commons-logging.jar and commons-modeler.jar to this directory.

To activate the connector use the console application to add the following JVM option to your server instance:

-Dcom.sun.enterprise.web.connector.enableJK=8009

Restart your application server.

All requests to the definded mount points on the webserver should now be delegated to one of your application servers.

You can download the sample configuration files, jar archives and mod_jk modules for windows and linux here.

Developing Java Mappings for SAP XI

You can use NetBeans to develop java mappings for the SAP NetWeaver Exchange Infrastructure (SAP XI). The only thing you need is the mapping api library from your SAP XI installation.

Registering Mapping API in NetBeans

Use the NetBeans Library Manager under «Tools->Library Manager» to register the aii_map_api.jar file. You find this library in the following path of your SAP XI installation:

<SAP_install_dir>/<system_name>/<instance_name>/j2ee/cluster/
server<number>/apps/sap.com/com.sap.xi.services/

Project settings

The SAP NetWeaver Exchange Infrastructure 3.0 is based on J2EE 1.3 and Java 1.4, so you must use a JDK 1.4.x and source level 1.4 for your project.

Useful libraries

SAP XI uses XML messages for message exchange. You can eather use the standard XML api libraries shipped with the JDK or you can use the pretty nice JDom open source library. I preffer JDom, because it has a build-in SAX builder and can also output XML as text (also formatted).

Mapping class

Your mapping class must implement the com.sap.aii.mapping.api.StreamTransformation interface. This interface requires you to implement the execute() and setParameter() methods.

Method execute()

The execute() method does the whole mapping. This method has two parameters of type java.io.InputStream and java.io.OutputStream. You read data from the input stream, make the necessary mapping and write the resulting data to the output stream.

SAP XI usually uses XML messages, but you can also read and write other type of data in your mapping, e.g. comma-separated values.

Method setParameter()

The integration engine uses the method setParameter() to inform the mapper about runtime configuration parameters. You can use the constants of the interface com.sap.aii.mapping.api.StreamTransformationConstants to access this parameters.

Sample mapping class for IDOC to file scenario

In this sample we map an IDOC to a plain text file.

XML representation of this IDOC

<?xml version="1.0" encoding="UTF-8" ?>
<ZADDRESS01>
  <IDOC BEGIN="1">
    <EDI_DC40 SEGMENT="1">
      <TABNAM>EDI_DC40</TABNAM>
      <MANDT>100</MANDT>
      <DOCNUM>0000000000000001</DOCNUM>
      <DOCREL>620</DOCREL>
      <STATUS>30</STATUS>
      <DIRECT>1</DIRECT>
      <OUTMOD>4</OUTMOD>
      <IDOCTYP>ZADDRESS01</IDOCTYP>
      <MESTYP>ZADDRESS</MESTYP>
      <MESCOD>DEV</MESCOD>
      <MESFCT>OUT</MESFCT>
      <SNDPOR>SAPDEV</SNDPOR>
      <SNDPRT>LS</SNDPRT>
      <SNDPRN>DEVCLNT100</SNDPRN>
      <RCVPOR>XID</RCVPOR>
      <RCVPRT>LS</RCVPRT>
      <RCVPRN>PARTNER1</RCVPRN>
      <CREDAT>20071028</CREDAT>
      <CRETIM>101658</CRETIM>
      <SERIAL>20071028101657</SERIAL>
    </EDI_DC40>
    <ZADDRESS SEGMENT="1">
      <VKORG>1000</VKORG>
      <TITLE>Mr.</TITLE>
      <LASTNAME>Doe</LASTNAME>
      <FIRSTNAME>John</FIRSTNAME>
      <STREET>Pier</STREET>
      <STREETNO>47</STREETNO>
      <CITY>San Francisco</CITY>
      <POSTLCODE>94133</POSTLCODE>
      <STATE>CA</STATE>
      <COUNTRY>US</COUNTRY>
    </ZADDRESS>
    <ZADDRESS SEGMENT="1">
      <VKORG>1000</VKORG>
      <TITLE>Mrs.</TITLE>
      <LASTNAME>Doe</LASTNAME>
      <FIRSTNAME>Jane</FIRSTNAME>
      <STREET>Mason St.</STREET>
      <STREETNO>950</STREETNO>
      <CITY>San Francisco</CITY>
      <POSTLCODE>94108</POSTLCODE>
      <STATE>CA</STATE>
      <COUNTRY>US</COUNTRY>
    </ZADDRESS>
  </IDOC>
</ZADDRESS01>

Source code of mapper:

package de.gascoyne.mapper;

import com.sap.aii.mapping.api.StreamTransformation;
import com.sap.aii.mapping.api.StreamTransformationConstants;
import com.sap.aii.mapping.api.StreamTransformationException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.Iterator;
import java.util.Map;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.input.SAXBuilder;

/**
 * Sample mapper for SAP-XI
 * @author Marcel Gascoyne
 */
public class AddressToTextMapper implements StreamTransformation {

  private String party = "";

  /**
   * Injection of mapping parameters
   * from integration engine
   *
   * @param map Map with configuration data
   */
  public void setParameter(Map map) {
    // Determine receiver party
    party = (String) map.get(
      StreamTransformationConstants.RECEIVER_PARTY );
  }

  /**
   * Mapping implementation
   *
   * @param inputStream Input data from integration engine
   * @param outputStream Output data to integration engine
   */
  public void execute(InputStream inputStream,
    OutputStream outputStream)
    throws StreamTransformationException {
    try {
      // get idoc from input stream
      SAXBuilder sb = new SAXBuilder();
      Document doc = sb.build( inputStream );
      Element idoc = doc.getRootElement().getChild( "IDOC" );

      // create PrintWriter for output stream
      PrintWriter out = new PrintWriter( outputStream );
      String header = "party;vkorg;title;lastname;firstname;" +
        "street;streetno;city;postlcode;state;country\n";
      out.print( header );

      // process all ZADDRESS segments
      for ( Iterator it = idoc.getChildren().iterator(); it.hasNext(); ) {
        Element element = (Element) it.next();

        if ( !element.getName().equals( "ZADDRESS" ) ) {
          continue;
        }

        // get data from segment ZADDRESS
        String vkorg = element.getChildText( "VKORG" );
        String title = element.getChildText( "TITLE" );
        String lastname = element.getChildText( "LASTNAME" );
        String firstname = element.getChildText( "FIRSTNAME" );
        String street = element.getChildText( "STREET" );
        String streetNo = element.getChildText( "STREETNO" );
        String city = element.getChildText( "CITY" );
        String postlCode = element.getChildText( "POSTLCODE" );
        String state = element.getChildText( "STATE" );
        String country = element.getChildText( "COUNTRY" );

        // create output line
        String line =
          vkorg + ";" +
          title + ";" +
          lastname + ";" +
          firstname + ";" +
          street + ";" +
          streetNo + ";" +
          city + ";" +
          postlCode + ";" +
          state + ";" +
          country + "\r\n";

        // Write line to output stream
        out.print( line );

      }

      out.flush();
      out.close();

    } catch ( Exception e ) {
      throw new StreamTransformationException( e.getMessage() );
    }
  }

}

Resulting text file:

party;vkorg;title;lastname;firstname;street;streetno;city;postlcode;state;country
PARTNER1;1000;Mr.;Doe;John;Pier;47;San Francisco;94133;CA;US
PARTNER1;1000;Mrs.;Doe;Jane;Mason St.;950;San Francisco;94108;CA;US

Sample mapping class for file to IDOC scenario

Now the opposite way. In this scenario we map a text file to an IDOC. We must create the whole IDOC structure from scratch in XML format. Many fields in the IDOC control record are mandantory fields and must be set, although the integration engine injects the correct values in this fields. We set this fields to the static text empty and the integration engine do the rest.

Input text file:

party;vkorg;title;lastname;firstname;street;streetno;city;postlcode;state;country
1000;Mr.;Doe;John;Pier;47;San Francisco;94133;CA;US
1000;Mrs.;Doe;Jane;Mason St.;950;San Francisco;94108;CA;US

Source code of java mapper:

package de.gascoyne.mapper;

import com.sap.aii.mapping.api.StreamTransformation;
import com.sap.aii.mapping.api.StreamTransformationConstants;
import com.sap.aii.mapping.api.StreamTransformationException;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.output.XMLOutputter;

/**
 * Sample mapper for SAP-XI
 * @author Marcel Gascoyne
 */
public class FileToIdocMapper implements StreamTransformation {

  private String party = "";

  /**
   * Injection of mapping parameters
   * from integration engine
   *
   * @param map Map with configuration data
   */
  public void setParameter(Map map) {
    // Determine receiver party
    party = (String) map.get(
      StreamTransformationConstants.RECEIVER_PARTY );
  }

  /**
   * Mapping implementation
   *
   * @param inputStream Input data from integration engine
   * @param outputStream Output data to integration engine
   */
  public void execute(InputStream inputStream,
    OutputStream outputStream)
    throws StreamTransformationException {
    try {
      InputStreamReader ir = new InputStreamReader( inputStream, "ISO-8859-1" );
      BufferedReader br = new BufferedReader( ir );

      // create control record
      SimpleDateFormat df = new SimpleDateFormat( "yyyyMMddHHmmss" );
      Element edi_dc40 = new Element( "EDI_DC40" );
      edi_dc40.setAttribute( "SEGMENT", "1" );
      edi_dc40.addContent( new Element( "TABNAM" ).addContent( "empty" ) );
      edi_dc40.addContent( new Element( "DIRECT" ).addContent( "empty" ) );
      edi_dc40.addContent( new Element( "IDOCTYP" ).addContent( "empty" ) );
      edi_dc40.addContent( new Element( "MESTYP" ).addContent( "empty" ) );
      edi_dc40.addContent( new Element( "SNDPOR" ).addContent( "empty" ) );
      edi_dc40.addContent( new Element( "SNDPRT" ).addContent( "empty" ) );
      edi_dc40.addContent( new Element( "SNDPRN" ).addContent( "empty" ) );
      edi_dc40.addContent( new Element( "RCVPOR" ).addContent( "empty" ) );
      edi_dc40.addContent( new Element( "RCVPRN" ).addContent( "empty" ) );
      edi_dc40.addContent( new Element( "SERIAL" ).addContent( df.format( new Date() ) ) );

      // create idoc
      Document zaddress01 = new Document();
      Element root = new Element( "ZADDRESS01" );
      zaddress01.setRootElement( root );
      Element idoc = new Element( "IDOC" );
      idoc.setAttribute( "BEGIN", "1" );
      idoc.addContent( edi_dc40 );
      root.addContent( idoc );

      while ( br.ready() ) {
        // get line of input stream
        String line = br.readLine();

        // check for header line
        if ( line.startsWith( "party;vkorg;" ) ) {
          continue;
        }

        // split values by semicolon in array
        String[] columns = line.split( ";" );

        // create ZADDRESS segment
        Element zaddress = new Element( "ZADDRESS" );
        zaddress.setAttribute( "SEGMENT", "1" );
        zaddress.addContent( new Element( "VKORG" ).setText( columns[1] ) );
        zaddress.addContent( new Element( "TITLE" ).setText( columns[2] ) );
        zaddress.addContent( new Element( "LASTNAME" ).setText( columns[3] ) );
        zaddress.addContent( new Element( "FIRSTNAME" ).setText( columns[4] ) );
        zaddress.addContent( new Element( "STREET" ).setText( columns[5] ) );
        zaddress.addContent( new Element( "CITY" ).setText( columns[6] ) );
        zaddress.addContent( new Element( "POSTLCODE" ).setText( columns[7] ) );
        zaddress.addContent( new Element( "STATE" ).setText( columns[8] ) );
        zaddress.addContent( new Element( "COUNTRY" ).setText( columns[9] ) );

        // add segment to idoc
        idoc.addContent( zaddress );
      }

      // write idoc to output stream
      PrintWriter out = new PrintWriter( outputStream );
      XMLOutputter xout = new XMLOutputter();
      xout.output( zaddress01, out );

    } catch ( Exception e ) {
      throw new StreamTransformationException( e.getMessage() );
    }
  }

}

Unit tests

To locally test your java mappings, you can create JUnit tests inside the NetBeans IDE. From within your mapping class select «Tools->Create JUnit Tests» to create a test case for your mapper.

This is a sample test case for an idoc-to-file mapper:

package de.gascoyne.mapper;

import com.sap.aii.mapping.api.StreamTransformationConstants;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import junit.framework.*;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;

/**
 * TestCase for AddressToTextMapper
 * @author Marcel Gascoyne
 */
public class AddressToTextMapperTest extends TestCase {

  public void testExecute() throws Exception {
    InputStream in = new FileInputStream( "build/test/classes/idoc_in.xml" );
    OutputStream out = new FileOutputStream( "build/test/results/text_out.txt" );
    AddressToTextMapper instance = new AddressToTextMapper();
    Map params = new HashMap();

    params.put( StreamTransformationConstants.RECEIVER_PARTY, "PARTY1" );
    instance.setParameter( params );
    instance.execute( in, out );
  }

}

NetBeans JPA and Hibernate Tutorial

This tutorial illustrates the usage of JPA with Hibernate as the persistence layer for a Java SE application. For this tutorial we use a PostgreSQL database. The installation of PostgreSQL and creation of the test database is not handled in this tutorial.

Start NetBeans and create a new Java project.

JPA Tutorial: Screenshot new project wizard

Name your project «JPATutorial» and deselect the «Create Main Class» checkbox. Click on «Finish» and the setup wizard creates your project. Right click the «Source Packages» note in the project explorer and select «New -> Java Package». Name the package «jpatutorial» and click «Finish».

Libraries

We need some additional libraries for our project. First we need the Hibernate Core and Hibernate Entity Manager libraries. You can download this libraries from the Hibernate website. Second, we need the jdbc drivers for our PostgreSQL database system. And last, we need the JPA libraries from the Java EE 5 SDK. You can download this from the Sun website or if you have a installed Sun Java System Application Server, you can simply use the javaee.jar from this installation.

Use the NetBeans Library Manager under «Tools -> Library Manager» to create a new class library. Name the library «Hibernate-JPA» and choose «Class Libraries» as Library type.

Add the following files to the library class path:

from Hibernate Core:
hibernate3.jar
lib/antlr-2.7.6.jar
lib/asm-attrs.jar
lib/asm.jar
lib/c3p0-0.9.1.jar
lib/cglib-2.1.3.jar
lib/commons-collections-2.1.1.jar
lib/commons-logging-1.0.4.jar
lib/concurrent-1.3.2.jar
lib/dom4j-1.6.1.jar
lib/ehcache-1.2.3.jar
lib/javassist.jar
lib/log4j-1.2.11.jar

from Hibernate Entity Manager:
hibernate-entitymanager.jar
lib/hibernate-annotations.jar
lib/hibernate-commons-annotations.jar
lib/jboss-archive-browsing.jar

from Java EE 5 SDK:
javaee.jar

from PostgreSQL JDBC driver:
postgresql-8.2-504.jdbc3.jar

JPA Tutorial: Screenshot library manager

Persistence Unit

Now we create the persistence.xml file, the main configuration file for a JPA application. Right click your project in the project explorer and choose «New -> File/Folder». Select the «Persistence» category and the file type «Persistence Unit». Click next, the wizard asks about the persistence unit name, the persistence library and the database connection. So we want to use Hibernate as our persistence provider and PostgreSQL as database backend, our choices should look like this:

JPA Tutorial: Screenshot persistence unit wizard

If your database connection is not already defined you can use the «New database connection…» wizard to create it.

Leave the setting «Create» for the table generation scheme. This automatically create needed tables in the database. Use «Drop and Create» if your tables should be deleted and newly created on every program start (good for unit tests). «None» is the preffered setting for production environment and does nothing on program start.

We want to make this a swing application. So right click the package and choose «New -> JFrame Form». Type «MainUI» as class name and click finish. The wizard generates our new swing class and starts the matisse gui builder.

Entity class

JPA uses entity classes to persist information. An entity class is a simple pojo with annotations. You should always implement the Serializable interface, although it’s not needed. Background: In an Java EE environment, entity objects are often transfered between an enterprise java bean and a web application. The enterprise bean lives in the ejb container and the web application in the web container. When objects are transfered between containers, they must be serializable.

Right click your «jpatutorial» package and choose «New -> File/Folder». Select «Entity Class» from the «Persistence» category and click next. Enter Address as class name and click finish.

Place the cursor above the constructor and add the following code:

private String customerNo;
private String lastname;
private String firstname;
private String street;
private String postcode;
private String city;

Right click in the editor window and choose «Refactor -> Encapsulate fields…» to create the getter and setter methods for our properties.

User interface

Now we need some gui components for the application. Drag and drop six JLabel and six JTextField components onto the frame.

Name the label components «customerNoLabel», «nameLabel», «firstnameLabel», «streetLabel», «postcodeLabel» and «cityLabel». Set the component text property to a corresponding value.

For the textfield components choose «customerNoText», «nameText», «firstnameText», «streetText», «postcodeText» and «cityText» as component name. Clear the text property of this components.

Drag two JButton components to the page, one named «Load» and the other named «Save». Set the name properties to «loadButton» and «saveButton».

Your frame should look like this:

JPA Tutorial: Screenshot MainUI frame

Store object

Select the Save button, right click and choose «Events -> Action -> actionPerformed». Enter the following code into the event handler:

private void saveButtonActionPerformed(ActionEvent evt) {
  Address address = new Address();
  address.setCustomerNo( customerNoText.getText() );
  address.setLastname( lastnameText.getText() );
  address.setFirstname( firstnameText.getText() );
  address.setStreet( streetText.getText() );
  address.setPostcode( postcodeText.getText() );
  address.setCity( cityText.getText() );

  EntityManagerFactory emf = Persistence.createEntityManagerFactory(
    "JPATutorialPU" );
  EntityManager em = emf.createEntityManager();
  em.getTransaction().begin();

  try {
    em.persist( address );
    em.getTransaction().commit();
  } catch (Exception e) {
    System.out.println( e.getMessage() );
    em.getTransaction().rollback();
  } finally {
    em.close();
  }
}

Right click in your source and select «Fix Imports» (or press ALT-SHIFT-F) to add the needed import statements to your source.

This code creates a Address object and fill it’s properties from our frame. Now we create an EntityManagerFactory for our persistence unit. With this factory we can create the needed EntityManager.

To persist our Address object we use the persist method of the EntityManager.

Retrieve object

Now we want to retrieve an object from the database. Right click the load button and select «Events -> Action -> actionPerformed». Enter the following code into the event handler:

private void loadButtonActionPerformed(java.awt.event.ActionEvent evt) {                                           

  EntityManagerFactory emf = Persistence.createEntityManagerFactory(
    "JPATutorialPU" );
  EntityManager em = emf.createEntityManager();
  em.getTransaction().begin();

  String customerNo = JOptionPane.showInputDialog(
    null,
    "Enter customer number",
    "Input",
    JOptionPane.QUESTION_MESSAGE);

  try {
    Query query = em.createQuery(
      "SELECT a FROM Address a WHERE a.customerNo = :customerNo");
    query.setParameter(
      "customerNo",
      customerNo);

    Address address = (Address) query.getSingleResult();

    customerNoText.setText( address.getCustomerNo() );
    lastnameText.setText( address.getLastname() );
    firstnameText.setText( address.getFirstname() );
    streetText.setText( address.getStreet() );
    postcodeText.setText( address.getPostcode() );
    cityText.setText( address.getCity() );

  } catch (Exception e) {
    System.out.println( e.getMessage() );
    em.getTransaction().rollback();
  } finally {
    em.close();
  }
}

Right click in your source and select «Fix Imports» (or press ALT-SHIFT-F) to add the needed import statements to your source. Select «javax.persistence.Query» as fully qualified Name for the Query class.

This code does the following: It asks the user about the customer number, creates a query object and sets the parameter «customerNo» to the entered value. The method «getSingleResult» of the query object returns the result object or an exception if no result was found.

The source code for this tutorial can be downloaded here.

Fill a treeview programmatically

You must build a collection of TreeNode objects and add this collection to the TreeView control. Each TreeNode object has several properties to set up the visual appearance and functionality.

Important TreeNode properties

expanded - Shows this node expaned with children
ext - Title text of the node
target - Target browser window
toolTip - Tooltip text to display
url - Hyperlink to navigate to
action - Name of method to call if user selects this node
actionListenerExpression - Expression for action listener method.

Adding icon to TreeNode

To add a icon to the TreeNode you must first create a ImageComponent. Now you can set the theme specific icon with the setIcon method. Use a constant of class ThemeImages for the icon name. Add the created ImageComponent as facet to the TreeNode.

Sample:

ImageComponent image = new ImageComponent();
image.setIcon( TreeImages.TREE_FOLDER );
treeNode.getFacets().put( treeNode.IMAGE_FACET_KEY, image );

Setting Action Expression for TreeNode

To set the Action Expression of a TreeNode you must use the method setActionExpression of the TreeNode object. This method needs a MethodExpression object as parameter.

Sample code:

ExpressionFactory ef = FacesContext.
  getCurrentInstance().
  getApplication().
  getExpressionFactory();
ELContext elContext = FacesContext.
  getCurrentInstance().
  getELContext();
MethodExpression me = ef.createMethodExpression(
  elContext,
  "#{Page1.treeNode_action}",
  String.class, new Class[]{} );
treeNode.setActionExpression( me );

JAAS with active directory authentication in a web application

This is a sample to use JAAS authentication with a windows active directory server. I use a Sun Java System Application Server, so the steps with other servers could be different.

Step 1: Defining LDAP realm

In this example you must define a LDAP realm named «ads-realm» with the following parameters:

Realm class:

com.sun.enterprise.security.auth.realm.ldap.LDAPReam

Properties:

directory            = ldap://ads.host.name:389
base-dn              = DC=ads,DC=domain,DC=com
search-bind-dn       = user
search-bind-password = password
search-filter        = (&(objectClass=user)(sAMAccountName=%s))
group-search-filter  = (&(objectClass=group)(member=%d))
jaas-context         = ldapRealm

You must change directory, base-dn, search-bind-dn and search-bind-password to your active directory configuration. The «search-bind-dn» and «search-bind-password» parameters are needed, because with default settings active directory doesn’t allow anonymous users to browse the directory.

Step 2: Setting the following JVM Switch for refferals

The following JVM switch is needed with active directory LDAP servers:

-Djava.naming.referral=follow

Add this switch to your server startup script or with the admin console.

Step 3a: Basic authentication

Add the following section to your web.xml or go to Step 3b for form
based authentication.

<login-config>
  <auth-method>BASIC</auth-method>
  <realm-name>ads-realm</realm-name>
</login-config>

Step 3b: Form based authentication

Add the following section to your web.xml:

<login-config>
  <auth-method>FORM</auth-method>
  <realm-name>ads-realm</realm-name>
  <form-login-config>
    <form-login-page>/login.html</form-login-page>
    <form-error-page>/login.html</form-error-page>
  </form-login-config>
</login-config>

Create the page /login.html with a least the following code:

<html>
  <head/>
  <body>
    <form action="j_security_check" method="post">
      Username: <input type="text" name="j_username"><br/>
      Password: <input type="password" name="j_password"><br/>
      <input type="submit" value="Login"/>
    </form>
  </body>
</html>

Step 4: Adding security role to web.xml

Add at least one security role to your web.xml, in this example «userRole».

<security-role>
  <role-name>userRole</role-name>
</security-role>

Step 5: Adding security constraint to web.xml

Now we must create a security constraint and the path to the pages we want to allow only authenticated access. In this sample the access to the folder /pages/ is resticted to authenticated users in role «userRole».

<security-constraint>
  <display-name>SecurityConstraint</display-name>
  <web-resource-colletion>
    <web-resource-name>SecuredFolder</web-resource-name>
      <url-pattern>/pages/*</url-pattern>
    </web-resource-collection>
    <auth-constraint>
      <role-name>userRole</role-name>
    </auth-constraint>
  <user-data-constraint>
    <transport-guarantee>NONE</transport-guarantee>
  </user-data-constraint>
</security-constraint>

Step 6: Create role mapping between active directory group and role

Role mappings are defined in sun-web.xml for the Sun Java System Application Server, so add the following section:

<security-role-mapping>
  <role-name>userRole</role-name>
  <group-name>users</group-name>
</security-role-mapping>

This maps the active directory group «users» to our role «userRole»,
so only users in the group «users» can access our secured folder.