Jan Luehe's Blog

Friday Apr 04, 2008

Converged Http Sessions in SailFin

Converged Http Sessions in SailFin

Converged Http Sessions in SailFin

Introduction

ConvergedHttpSession is a new feature introduced by JSR 289, which provides access to SIP protocol sessions from HTTP and vice versa. This enables the creation of unified, cross-protocol communication services, where SIP calls may be initiated and terminated from a web browser.

A popular example of such a use case is the Click-to-dial application (see below), which enables a client to initiate a SIP call by simply clicking on a URL link.

A ConvergedHttpSession implements the javax.servlet.sip.ConvergedHttpSession interface, which in turn extends the javax.servlet.http.HttpSession interface defined by the Servlet specification.

This blog explains the motivation behind a converged application, provides a step-by-step analysis of how a ConvergedHttpSession may be used to initiate and terminate a SIP call from the HTTP servlet of a converged application, explains how the routing decisions of SailFin's Converged Loadbalancer influence the session id of a SipApplicationSession created from a ConvergedHttpSession, and finally shows how a ConvergedHttpSession fits into SailFin's in-memory replication framework that provides high-availability of HTTP and SIP artifacts in a SailFin cluster.

Converged Applications

A converged application combines SIP protocol functionality with other protocols (generally HTTP) to provide a unified communication service. It bundles both sip.xml and web.xml deployment descriptors for its SIP and HTTP servlet components, respectively.

In a JSR 289 compliant container, such as SailFin, HTTP sessions are exposed to HTTP servlets either as instances of javax.servlet.http.HttpSession or javax.servlet.sip.ConvergedHttpSession, depending on the type of application: In the case of a pure web application (i.e., one that has a web.xml, but no sip.xml), HTTP sessions are exposed as instances of javax.servlet.http.HttpSession, whereas in the case of a converged application, they are exposed as instances of javax.servlet.sip.ConvergedHttpSession.

This means that developers of HTTP servlets that are part of a converged application may cast the result of a call to HttpServletRequest.getSession() to javax.servlet.sip.ConvergedHttpSession, as shown in the following code snippet:

  import java.io.IOException;
  import javax.servlet.ServletException;
  import javax.servlet.http.HttpServlet;
  import javax.servlet.http.HttpServletRequest;
  import javax.servlet.http.HttpServletResponse;
  import javax.servlet.sip.ConvergedHttpSession;

  public class ConvergedServlet extends HttpServlet {

      public void doGet(HttpServletRequest httpReq, HttpServletResponse httpRes)
              throws ServletException, IOException {

          ConvergedHttpSession chs = (ConvergedHttpSession) httpReq.getSession();
          [...]
      }

      [...]
  }

If invoked from the HTTP servlet of a pure web application, the above cast to ConvergedHttpSession would result in a ClassCastException.

Through its ConvergedHttpSession interface, an HttpSession may be associated with (that is, added as a child protocol session to) a javax.servlet.sip.SipApplicationSession. Once this type of association has occurred, the ConvergedHttpSession interface allows navigation from the HttpSession to its SipApplicationSession parent and SipSession siblings, as shown in the following code snippet:

  import javax.servlet.sip.ConvergedHttpSession;
  import javax.servlet.sip.SipApplicationSession;
  import javax.servlet.sip.SipSession;

  ConvergedHttpSession chs = (ConvergedHttpSession) httpReq.getSession();
  SipApplicationSession sas = chs.getApplicationSession();
  Iterator sipSessions = sas.getSessions("SIP");

Click-To-Dial Use Case

The following example assumes a converged application with one HTTP servlet (ClickToDialServlet) and one SIP servlet (CallServlet). The example focuses on the HTTP servlet, because it is more relevant in terms of the use of ConvergedHttpSession.

The ClickToDialServlet allows to initiate and terminate a SIP call from sip:foo@<localIp>:<localSipPort> to sip:bar@<remoteIp>:<remoteSipPort>, where remoteIp and remoteSipPort are passed in as HTTP request parameters. It either initiates or terminates a SIP call, based on the value of the action request parameter:

  @Resource(mappedName = "sip/convergedapp")
  private SipFactory sipFactory;

  public class ClickToDialServlet extends HttpServlet {

      public void doGet(HttpServletRequest httpReq, HttpServletResponse httpRes)
            throws ServletException, IOException {

          // Initiate or terminate a SIP call, depending on the value of the
          // "action" request parameter
          String action = httpReq.getParameter("action");
          if (action.equals("call")) {
              initiateSipCall(httpReq, httpRes);
          } else if (action.equals("bye")) {
              terminateSipCall(httpReq, httpRes);
          } else {
              throw new ServletException("Invalid action");
          }
      }

      [...]
  }

The initiateSipCall and terminateSipCall methods of the ClickToDialServlet are implemented as follows:

  /**
   * Initiates a SIP call
   */
  private void initiateSipCall(HttpServletRequest httpReq, HttpServletResponse httpRes) throws ServletException, IOException {

      // Create a new ConvergedHttpSession
      ConvergedHttpSession chs = (ConvergedHttpSession) httpReq.getSession();

      // Create a new SipApplicationSession, and link the ConvergedHttpSession to it
      SipApplicationSession sas = chs.getApplicationSession();

      // Create the SIP URIs that will be included in the To and From fields of the SIP INVITE request.
      // The SipFactory to create the SIP URIs has been injected into this servlet, using the @Resource annotation (see above)
      String localIp = InetAddress.getLocalHost().getAddress().toString();
      Address fromAddr = sipFactory.createAddress("sip:foo@" + localIp + ":" + System.getProperty("SIP_PORT"));
      Address toAddr = sipFactory.createAddress("sip:bar@" + httpReq.getParameter("remoteIp") + ":" + httpReq.getParameter("remotePort"));

      // Create the SIP INVITE request using the SipFactory and the toAddr and fromAddr addresses
      SipServletRequest sipReq = sipFactory.createRequest(sas, "INVITE", fromAddr, toAddr);

      // Create a SipSession
      SipSession sipSession = sipReq.getSession();

      // Have any requests and responses related to the new SipSession be handled by the converged application's CallServlet
      sipSession.setHandler("CallServlet");

      // Store the id of the SipSession as a session attribute with name "CallId" in the parent SipApplicationSession,
      // so that this SipSession may later be found and used to terminate the call
      sas.setAttribute("CallId", sipSession.getId());

      // Send the request
      sipReq.send();

      // Return a web form that may be used to terminate the call
      PrintWriter out = httpRes.getWriter();
      out.println("<form action=\"ClickToDialServlet\" method=\"GET\">");
      out.println("<input type=\"hidden\" name=\"action\" value=\"bye\"/>");
      out.println("<input type=\"submit\" value=\"Terminate call\"/>");
      out.println("</form>");
  }

  /**
   * Terminates a SIP call.
   *
   * This method is invoked when the FORM returned by the initiateSipCall
   * method of this servlet is submitted.
   */
  private void terminateSipCall(HttpServletRequest httpReq, HttpServletResponse httpRes) throws ServletException, IOException {

      // Resume the ConvergedHttpSession, which is identified by the JSESSIONID cookie sent with the request
      ConvergedHttpSession chs = (ConvergedHttpSession) httpReq.getSession(false);

      // Get the SipApplicationSession parent
      SipApplicationSession sas = chs.getApplicationSession();

      // Get the SipSession whose id had been stored as a session attribute with name "CallId" in the parent SipApplicationSession
      SipSession sipSession = sas.getSipSession((String) sas.getAttribute("CallId"));

      // Send a BYE to the UAS to terminate the call
      sipSession.createRequest("BYE").send();
  }

Colocation Requirements

Calling getApplicationSession() on a ConvergedHttpSession that has not yet been associated with any SipApplicationSession will create a new SipApplicationSession and make it the parent of the ConvergedHttpSession, unless the HTTP request URL that was used to create or resume the ConvergedHttpSession contains a parameter with name sipappsessionid, in which case the corresponding SipApplicationSession will be looked up and made the parent of the ConvergedHttpSession.

If ConvergedHttpSession.getApplicationSession() ends up creating a SipApplicationSession in a SailFin cluster, and the cluster is load-balanced by the Converged Loadbalancer (CLB) with its consistent-hash load-balancing algorithm enabled, special precautions are taken by SailFin's converged container as it forms the id of the new SipApplicationSession.

In SailFin, the id of a SipApplicationSession consists of three components:

  • a key component, which is used to determine the instance where (according to the consistent hash algorithm) the SipApplicationSession belongs,
  • an appName, which identifies the application to which the SipApplicationSession belongs, and
  • a random uuid.

In its consistent-hash mode, the CLB computes a hashkey from the header values of an initial request, based on the rules specified in the Data Centric Rules (DCR) configuration file. This hashkey is used to look up, with the help of a consistent hash function, the cluster instance that is supposed to service the request, and the CLB then forwards the request to this instance. The hashkey, which is computed only once per initial request, is included in the response in the form of a BEKey.

For example, in the case of a 200 OK SIP response, the BEKey is included in the Contact header, and in the case of a SIP response with a status code greater than or equal to 300, it is included in the To header. In the case of a HTTP response, the BEKey is returned as a cookie, or encoded in the URL if cookies have been disabled.

The presence of a BEKey in subsequent requests ensures that these requests will be routed by the CLB to the same instance that processed the initial request, unless that instance has gone down in the meantime, in which case the BEKey will have been remapped to a different instance.

See the CLB Functional Specification and blog for details.

For example, the submission of the FORM returned by the initiateSipCall method of the ClickToDialServlet in the above example may result in the following HTTP request:

  GET /convergedapp/ClickToDialServlet?action=bye HTTP/1.1
  Host: [...]
  Cookie: BEKey=hashkey; JSESSIONIDVERSION=/convergedapp:0; JSESSIONID=0045773156aee0763caf8862c242
  [...]

Notice how the BEKey is carried in the request's Cookie header.

When the CLB intercepts and proxies an initial or subsequent HTTP request, it adds the BEKey as a header with name proxy-bekey to the HTTP request.

If a call to ConvergedHttpSession.getApplicationSession() ends up creating a new SipApplicationSession, SailFin's converged container checks the HTTP request for the proxy-bekey header, and if present, assigns its value to the key component of the id of the newly generated SipApplicationSession.

This ensures that the instance to which a request that resumes a ConvergedHttpSession is routed by the CLB, and the instance on which the ConvergedHttpSession's associated SipApplicationSession belongs (according to the consistent hash algorithm), always match.

High-Availability

SailFin provides high-availability of SIP and HTTP session artifacts in a cluster of SailFin instances, by leveraging and extending the in-memory replication feature that was first introduced in GlassFish v2. SailFin's high-availability feature, which is also known under the acronym SSR (Sip Session Retainability), will be covered in a separate blog.

Once a ConvergedHttpSession has been associated with a SipApplicationSession, the session id of the SipApplicationSession is stored as an instance field in the ConvergedHttpSession and becomes part of that session's serialized representation. The fact that an HTTP session being replicated may really be an instance of javax.servlet.sip.ConvergedHttpSession remains hidden from the in-memory replication framework.

Should a ConvergedHttpSession become separated from its parent SipApplicationSession in a cluster after a failover, it will be possible for the ConvergedHttpSession to retrieve its parent SipApplicationSession, based on its id.

Tuesday Feb 12, 2008

Alternate Docroots and Local Resource Paths

Alternate Docroots and Local Resource Paths

Alternate Docroots and Local Resource Paths

Judging from the traffic on the various GlassFish mailing lists and user forums, it seems that support for alternate docroots in GlassFish has become very popular.

However, from the feedback given, it looks like the way alternate docroot paths are computed by the web container deserves additional coverage: Several developers have tried leveraging alternate docroots and were left wondering why the container would return a 404 response instead of the contents of the requested resource.

Hopefully, this blog will clarify things.

When configuring an alternate docroot, try keeping in mind the following rules:

  • Alternate docroots (more specifically, their "from" values) are matched against a request's path info (obtained by calling javax.servlet.http.HttpServletRequest.getPathInfo()).
  • The local path of a resource whose request has been matched by an alternate docroot is computed by appending the request's path info to the alternate docroot's "dir" value.

As an example, consider the following alternate docroot declaration in sun-web.xml:

  <property name="alternatedocroot_1" value="from=/orderstore/* dir=C:/stryker_cci/orderstore"/>

and this request URL:

  http://localhost:8080/CIWeb/orderstore/test.txt

Assume the request is mapped to a web application deployed at /CIWeb, meaning the request URL's context root component is given as /CIWeb, and its path info component is given as /orderstore/test.txt, which is matched by the above alternate docroot. The local filesystem path where the requested resource will be looked up is given as the value of the alternate docroot's "dir" value:

  C:/stryker_cci/orderstore

with the request's path info:

  /orderstore/test.txt

appended to it, resulting in:

  C:/stryker_cci/orderstore/orderstore/test.txt

As another example, consider the following alternate docroot declaration in sun-web.xml:

  <property name="alternatedocroot_1" value="from=/myimages/* dir=/images"/>

and this request URL:

  http://localhost:8080/myimages/image1.jpg

Further assume that the above request is mapped to a web application deployed at the root context ("/"). In this case, the request's path info evaluates to:

 /myimages/image1.jpg

meaning it is matched by the above alternate docroot. The local filesystem path where the requested resource will be looked up is given as the value of the alternate docroot's "dir" value:

  /images

with the request's path info:

  /myimages/image1.jpg

appended to it, resulting in:

  /images/myimages/image1.jpg

Friday Dec 14, 2007

GlassFish Support for Cookie-less HTTP Sessions

GlassFish Support for Cookie-less HTTP Sessions

GlassFish Support for Cookie-less HTTP Sessions

Recently, we have come across an interesting technical requirement posted to users@glassfish.dev.java.net by Evaristo Jose Camarero:

The technical requirement centered around the ability to create and resume HTTP sessions from mobile client devices, without the ability to leverage HTTP cookies or URL rewriting facilities. Instead, HTTP sessions would be identified by the mobile clients' IP addresses. The inherent limitation that each mobile device could handle only one session (per web application) at a time was deemed acceptable. Would it be possible to support such a resource constrained environment with GlassFish? The answer is: Yes!

In GlassFish V2, we added support for allowing web application developers to extend the web container by injecting their own custom valves into the container's request and response processing framework.

I realized that we could leverage this extension mechanism in combination with a special session management configuration property to support the above requirement: What if we developed an application-specific valve that sets the client's IP address (available via the standard javax.servlet.ServletRequest.getRemoteAddr() Servlet API method) as the requested session id on every intercepted request?

The following valve implementation code illustrates the solution:

package my.package;

import java.io.IOException;
import javax.servlet.ServletException;
import org.apache.catalina.Request;
import org.apache.catalina.Response;
import org.apache.catalina.Valve;
import org.apache.coyote.tomcat5.CoyoteRequest;

/**
 * Valve responsible for setting the client's IP address as the HTTP session id.
 */
public class SessionIdValve implements Valve {

    private static final String INFO = "Valve responsible for setting the client's IP address as the HTTP session id";

    public String getInfo() {
        return INFO;
    }

    /**
     * Invoked "on the way in".
     */
    public int invoke(Request request, Response response)
            throws IOException, ServletException {
        CoyoteRequest coyoReq = (CoyoteRequest) request;
        coyoReq.setRequestedSessionId(coyoReq.getRemoteAddr());
        return INVOKE_NEXT;
    }

    /**
     * Invoked "on the way out".
     */
    public void postInvoke(Request request, Response response)
            throws IOException, ServletException {
        // Deliberate no-op
    }
}

Since an application-specific valve is given the opportunity to intercept any request that has been routed to the web application before the request is passed to application code, the above valve can be used to assign the client's IP address to any new HTTP session generated for the client, and to resume an existing HTTP session on behalf of the client.

To make the above solution work, the following sun-web.xml deployment descriptor must be bundled in the web application's WEB-INF directory:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE sun-web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Application Server 8.1 Servlet 2.4//EN" "http://www.sun.com/software/sunone/appserver/dtds/sun-web-app_2_4-1.dtd">

<sun-web-app>
  <property name="valve_1" value="my.package.SessionIdValve"/>
  <property name="reuseSessionID" value="true"/>
</sun-web-app>

The property with name valve_1 causes the valve whose fully qualified name my.package.SessionIdValve is given as the property's value, and whose class file must be available in the web application's WEB-INF/classes directory, or in one of the JAR files in the web application's WEB-INF/lib directory, to be instantiated during deployment and injected into the web application's request processing path.

The other property: reuseSessionID, allows the valve to instruct the session manager (assigned by the web container to the web application) to use the client's IP address as the session id for any new sessions, rather than generating a random value.

Evaristo tried the proposed solution in his environment and reported back that he was very satisfied with it.

This solution may also be used in combination with a GlassFish cluster and its in-memory session replication feature.

Monday Dec 03, 2007

How to Loadbalance GlassFish Cluster with Apache Loadbalancer

How to Loadbalance GlassFish Cluster with Apache Loadbalancer

How to Loadbalance GlassFish Cluster with Apache Loadbalancer

Since GlassFish V1, it has been possible to front-end a GlassFish instance with Apache's httpd web server, after following a few simple configuration steps, which include defining the com.sun.enterprise.web.connector.enableJK system property on the GlassFish instance, and specifying the port number of the mod_jk listener on the GlassFish instance as its value. By specifying this system property, the mod_jk connector, which comes standard with GlassFish (minus the JAR files that need to be copied from a Tomcat installation as per the configuration steps referenced above), will be started automatically and will listen on the specified port to any traffic sent by the httpd front-end over the AJP protocol. (Please notice that when you follow the configuration steps referenced above, you must use the tomcat-ajp.jar from Tomcat 5.5.23. Using the tomcat-ajp.jar bundled with a more recent Tomcat release will not work.)

A common use case for front-ending GlassFish with httpd is to have httpd serve any requests for static resources, while having any requests for dynamic resources, such as servlets and JavaServer(TM) Pages (JSPs), forwarded to, and handled by the GlassFish backend instance.

However, up until now, support for Apache's httpd has been limited to a single GlassFish instance, and there has been great interest on the GlassFish user forum in having an entire cluster of GlassFish instances load-balanced by Apache, allowing users to transition from an Apache-loadbalanced cluster of Tomcat instances to an Apache-loadbalanced cluster of GlassFish instances and take advantage of the in-memory session replication feature introduced in GlassFish V2.

We have listened to the GlassFish user community and added the requested feature to the SJSAS 9.1 UR1 release. In other words, with the upcoming SJSAS 9.1 UR1 release, it will be possible to load-balance a cluster of GlassFish instances with Apache.

In order to support stickiness, Apache's loadbalancer relies on a jvmRoute to be included in any JSESSIONID received by it. The jvmRoute, which is separated from the session id via ".", and whose value is configured via a system property of the same name, identifies the cluster instance on which the HTTP session was generated, or on which it was last resumed. This means that every GlassFish instance in a cluster that is front-ended by Apache's loadbalancer must be configured with a jvmRoute system property whose value is unique within the cluster.

For example, if an HTTP session was generated on a cluster instance with a jvmRoute system property equal to instance1, the JSESSIONID returned to the client (via an HTTP cookie or URL rewriting) will contain the session id with the string .instance1 appended to it. A subsequent request that is intercepted by the Apache loadbalancer will include the same JSESSIONID value that was returned to the client, from whose jvmRoute suffix the Apache loadbalancer can determine the instance on which the HTTP session was last served, and direct the request to it. Should that instance have failed in the meantime, the Apache loadbalancer will select a different instance from the remaining healthy instances, and have the request failover to it. For example, if the request fails over to an instance whose jvmRoute system property is equal to instance2, the response generated from that instance will include a JSESSIONID containing the session id with .instance2 (instead of .instance1) appended to it.

The challenge we were facing when adding support for the jvmRoute feature to GlassFish has been that while the Apache loadbalancer expects the jvmRoute, whose value may change over the lifetime of its associated HTTP session, to be part of the JSESSIONID, we had to shield the session management in GlassFish from the jvmRoute, to preserve the invariant (from the session management's perspective) that session ids are immutable and remain constant over the lifetime of a session.

We've addressed this challenge by having the web container strip any jvmRoute off an incoming JSESSIONID (and use the remainder as the session id of the session to be resumed), and append a jvmRoute to the session id when forming a JSESSIONID. Of course, we have the web container process a JSESSIONID in this way only if the jvmRoute system property has been set.

One of the side effects of this change has been that since a jvmRoute is dynamic, the web container now adds a JSESSIONID cookie to every response, regardless of whether an HTTP session was created or resumed by the corresponding request, provided that the jvmRoute system property has been set.

The remainder of this blog covers important configuration aspects.

In order to load-balance a GlassFish cluster via Apache, follow these steps:

  1. Define the jvmRoute and com.sun.enterprise.web.connector.enableJK system properties at the GlassFish cluster level. For example, in the case of a cluster named "cluster1", run these commands:
      asadmin create-jvm-options --target cluster1 "-DjvmRoute=\${AJP_INSTANCE_NAME}"
      asadmin create-jvm-options --target cluster1 "-Dcom.sun.enterprise.web.connector.enableJK=\${AJP_PORT}"
    
  2. Configure the above system properties for each instance in the cluster. For example, for a cluster instance named "instance9", run these commands:
      asadmin create-system-properties --target instance9 AJP_INSTANCE_NAME=instance9
      asadmin create-system-properties --target instance9 AJP_PORT=8020
    
    Notice how the port number (8020) specified for the mod_jk connector on "instance9" matches the value of the corresponding worker.instance9.port in the sample workers.properties below.

  3. List each GlassFish instance, including the port number of its mod_jk connector, in Apache's workers.properties configuration file. Make sure that the name of each worker equals the value of the jvmRoute system property of the GlassFish instance to which the worker connects. This convention makes it possible for an HTTP session to remain sticky to the GlassFish instance on which the session was created, or on which the session was last resumed.

  4. The following sample workers.properties configuration file is used to load-balance a 9-instance GlassFish cluster, in which the instances are spread over three physical server machines: my.domain1.com, my.domain2.com, and my.domain3.com:
      # Define 1 real worker using ajp13
      worker.list=loadbalancer
      # Set properties for instance1
      worker.instance1.type=ajp13
      worker.instance1.host=my.domain1.com
      worker.instance1.port=8012
      worker.instance1.lbfactor=50
      worker.instance1.cachesize=10
      worker.instance1.cache_timeout=600
      worker.instance1.socket_keepalive=1
      worker.instance1.socket_timeout=300
      # Set properties for instance4
      worker.instance4.type=ajp13
      worker.instance4.host=my.domain1.com
      worker.instance4.port=8015
      worker.instance4.lbfactor=50
      worker.instance4.cachesize=10
      worker.instance4.cache_timeout=600
      worker.instance4.socket_keepalive=1
      worker.instance4.socket_timeout=300
      # Set properties for instance7
      worker.instance7.type=ajp13
      worker.instance7.host=my.domain1.com
      worker.instance7.port=8018
      worker.instance7.lbfactor=50
      worker.instance7.cachesize=10
      worker.instance7.cache_timeout=600
      worker.instance7.socket_keepalive=1
      worker.instance7.socket_timeout=300
      # Set properties for instance2
      worker.instance2.type=ajp13
      worker.instance2.host=my.domain2.com
      worker.instance2.port=8013
      worker.instance2.lbfactor=50
      worker.instance2.cachesize=10
      worker.instance2.cache_timeout=600
      worker.instance2.socket_keepalive=1
      worker.instance2.socket_timeout=300
      # Set properties for instance5
      worker.instance5.type=ajp13
      worker.instance5.host=my.domain2.com
      worker.instance5.port=8016
      worker.instance5.lbfactor=50
      worker.instance5.cachesize=10
      worker.instance5.cache_timeout=600
      worker.instance5.socket_keepalive=1
      worker.instance5.socket_timeout=300
      # Set properties for instance8
      worker.instance8.type=ajp13
      worker.instance8.host=my.domain2.com
      worker.instance8.port=8019
      worker.instance8.lbfactor=50
      worker.instance8.cachesize=10
      worker.instance8.cache_timeout=600
      worker.instance8.socket_keepalive=1
      worker.instance8.socket_timeout=300
      # Set properties for instance3
      worker.instance3.type=ajp13
      worker.instance3.host=my.domain3.com
      worker.instance3.port=8014
      worker.instance3.lbfactor=50
      worker.instance3.cachesize=10
      worker.instance3.cache_timeout=600
      worker.instance3.socket_keepalive=1
      worker.instance3.socket_timeout=300
      # Set properties for instance6
      worker.instance6.type=ajp13
      worker.instance6.host=my.domain3.com
      worker.instance6.port=8017
      worker.instance6.lbfactor=50
      worker.instance6.cachesize=10
      worker.instance6.cache_timeout=600
      worker.instance6.socket_keepalive=1
      worker.instance6.socket_timeout=300
      # Set properties for instance9
      worker.instance9.type=ajp13
      worker.instance9.host=my.domain3.com
      worker.instance9.port=8020
      worker.instance9.lbfactor=50
      worker.instance9.cachesize=10
      worker.instance9.cache_timeout=600
      worker.instance9.socket_keepalive=1
      worker.instance9.socket_timeout=300
    
      worker.loadbalancer.type=lb
      worker.loadbalancer.balance_workers=instance1,instance2,instance3,instance4,instance5,instance6,instance7,instance8,instance9
    
  5. Reference the loadbalancer worker specified in your workers.properties file from your httpd.conf. The following snippet from httpd.conf causes any JSP requests to be load-balanced over the GlassFish cluster configured in the above workers.properties file:
      JkWorkersFile workers.properties
      # Loadbalance all JSP requests over GlassFish cluster
      JkMount /*.jsp loadbalancer
    

As soon as the cluster instance to which an HTTP session has been sticky has failed, the loadbalancer will route any subsequent requests for the same HTTP session to a different instance. This instance will be able to load and resume the requested session using the in-memory session replication feature that has been available since GlassFish V2. The in-memory session replication feature is enabled only for those web applications that have been marked as distributable in their web.xml deployment descriptor, and that have been deployed to the cluster with the --availabilityenabled option of the asadmin deploy command set to true (default is false).

Monday Sep 17, 2007

New Web Container Features in GlassFish V2

New Web Container Features in GlassFish V2

New Web Container Features in GlassFish V2

GlassFish V2 has been released today. Following is a short summary of the new web container features available in this release (please see Jeanfrancois Arcand's blog for new Grizzly related features in GlassFish V2, including support for Comet and HTTP compression):
  • Alternate docroots:

    Alternate docroots allow a virtual server or web application to use docroots other than the docroot assigned to them by the web container. Alternate docroots are configured with, and matched by, request URL patterns, similar to how requests are mapped to servlets. Alternate docroots may be configured either at the virtual server level or for individual web applications.

    One of the many use cases made possible by alternate docroots is the sharing of resources across multiple web applications, by storing these resources in alternate docroots instead of bundling them with each web application.

  • Dynamic reconfiguration:

    All aspects of the web container, including the creation and removal of HTTP listeners and virtual servers, as well as any changes to existing HTTP listeners and virtual servers, are now dynamically reconfigurable, meaning they no longer require any server restart in order to take effect.

  • In-memory compilation of JavaServer(TM) Pages (JSPs):

    The JSP compiler has taken advantage of the Java(TM) Compiler API, which has been standarized under JSR 199. This means that the compilation of the servlet derived from a JSP page may now be performed programmatically by invoking the Java Compiler API, instead of by running "javac" on a forked process. This also means that the generated .java and .class files no longer need to be written to, or read from, disk. Overall, this feature, which is available only when using a Java 6 based runtime, has improved the first-access response time of JSP pages dramatically. See Kin-Man Chung's blog for details.

  • In-memory replication of HTTP sessions:

    GlassFish V2 supports a new session persistence type called in-memory replication, which provides high availability and failover capabilities of HTTP sessions and stateful session beans in a cluster environment, without persisting the sessions to a database (as is the case with HADB). The web container has added versioning support to HTTP sessions, to allow the replication framework to detect a failover situation, identify stale session data, and fetch, either from its local replication cache or from another instance in the cluster, the most recent version of an HTTP session that would satisfy the client request.

  • Smoother migration support from Tomcat:

    In order to allow for a smoother migration from Tomcat, GlassFish V2 now detects and processes Tomcat's context.xml configuration files. See Amy Roh's blog for details.

  • Improved I18N support:

    GlassFish V2 has improved support for web applications with multi-byte requirements. For example, multi-byte request URLs are now supported out-of-the-box, by using UTF-8 (instead of ISO-8859-1) as the default URL decoding. Alternative decodings may now be specified through a configuration property at the http-service and http-listener levels in domain.xml. In addition, multi-byte context roots and RFC 3490 style, internationalized domain names are now supported.

  • Virtual server security realms:

    A virtual server may now be configured with its own security realm, avoiding the need for individual web applications deployed on the virtual server to specify their own: They simply inherit the security realm of the virtual server on which they have been deployed, but can still override it with their own (as specified in their deployment descriptor) if needed.

  • Self-contained Expression Language:

    The JSP 2.1 Specification (JSR 245) has undergone a Maintenance Release (MR) in an effort to take the Expression Language (EL) out of JSP 2.1 and turn it into a self-contained specification.

  • Support for pluggable network authentication modules:

    Glassfish V2 is the reference implementation for a new standard, the Authentication SPI for Containers (aka, JSR 196). This standard is a general purpose framework which extends the pluggable authentication model of JAAS to the authentication of network messages. Support by the Glassfish web container for the Servlet profile of the 196 standard exposes a simple contract by which implementations of network authentication mechanisms can be configured (in the field) for use by the Glassfish web container in its processing of security contraints on behalf of portable web applications. The JSR 196 standard is available on the jcp web site.

  • Various performance optimizations:

    We've improved performance at various levels, both during web container startup as well as during normal request processing.

    For example, all web modules are now started concurrently when the web container is brought up.

    In addition, a virtual server's default web module is no longer implemented as a separate web module (it used to be implemented as an exact copy of the web module that had been declared as the virtual server's default web module). Instead, we've enhanced the container's request mapping algorithm by making it default web module aware, so that any requests mapped to the virtual server's root context are now routed to the web module that is acting as the virtual server's default web module (if any default web module has been configured for the virtual server). This has improved the container's startup performance, since it reduces the number of web modules that need to be started. In some cases, as with the admingui (which acts as the default web module of the admin virtual server), the savings have been significant, since the admingui used to initialize the JavaServer Faces (JSF) runtime during its startup, meaning the JSF runtime used to be initialized twice during server startup - and initializing the JSF runtime is not cheap!

    We've not only avoided initializing the admingui's JSF runtime twice during startup, but we actually went one step further by deferring the initialization of the admingui's JSF runtime to the time when the first request for the admingui is received.

    See Prashanth Abbagani's blog for how (in numbers) each of the above optimizations has contributed to improving startup performance.

    We also improved the way the Servlet and JSP containers cooperate, by having the Servlet container pass information about a web application's Tag Library Descriptor (TLD) related mappings to the JSP container, so that the JSP container does not need to parse this information again.

    In addition, the JSP compiler now creates a JSP tag library instance only once per web application (so that it may be shared by all the JSP pages in the web application that import it via a taglib directive or namespace declaration), instead of once per translation unit, which has improved JSP performance and reduced memory footprint.

Friday Apr 27, 2007

How a Jruby-Based Web Application Became Unusable Following its Redeployment. Lessons Learned

How a Jruby-Based Web Application Became Unusable Following its Redeployment. Lessons Learned

How a Jruby-Based Web Application Became Unusable Following its Redeployment. Lessons Learned

    Recently, Ashish Sahni, a GlassFish developer, reported a mysterious problem: He had deployed a Jruby-based web application to GlassFish, and when he tried to redeploy it, he noticed errors of the form java.lang.ThreadDeath in the server.log (in fact, the subsequent deployment of any web application would cause this error to be logged), and his web application had become unusable.

    How could the deployment and undeployment of a Jruby-based web application affect the subsequent deployment of unrelated web applications? And worse, how could it render the Jruby-based web application unusable?

    To investigate this issue, I looked at the stacktrace of the ThreadDeath error that was logged during redeployment:

    
      java.lang.ThreadDeath
        at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1325)
        ...
        at java.lang.Class.newInstance(Class.java:303)
        at java.security.Provider$Service.newInstance(Provider.java:1130)
        at sun.security.jca.GetInstance.getInstance(GetInstance.java:220)
        at sun.security.jca.GetInstance.getInstance(GetInstance.java:147)
        at java.security.Security.getImpl(Security.java:658)
        at java.security.MessageDigest.getInstance(MessageDigest.java:122)
        at java.io.ObjectStreamClass.computeDefaultSUID(ObjectStreamClass.java:1731)
        ...
        at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:302)
        at com.sun.enterprise.instance.SerializedDescriptorHelper.storeSerializedDescriptor(SerializedDescriptorHelper.java:239)
        at com.sun.enterprise.instance.SerializedDescriptorHelper.store(SerializedDescriptorHelper.java:143)
        at com.sun.enterprise.instance.SerializedDescriptorHelper.store(SerializedDescriptorHelper.java:123)
        at com.sun.enterprise.instance.BaseManager.saveAppDescriptor(BaseManager.java:684)
        ...
        at com.sun.enterprise.management.deploy.DeployThread.deploy(DeployThread.java:174)
        at com.sun.enterprise.management.deploy.DeployThread.run(DeployThread.java:210)
    

    I noticed the ThreadDeath error was thrown when the deployment code was attempting to serialize the web application's descriptor files, and in doing so, requested a MessageDigest instance for computing a serialization UID.

    The serialization (and deserialization) of application descriptors has been a performance enhancement developed by the deployment team. The code catches and logs any Throwable (along with its stacktrace) that may emerge from the attempt to serialize an application descriptor, and continues. Although the next reload of an application will take longer without the serialized descriptor, an exception during serialization does not cause any functional problems, i.e., the application will still deploy fine.

    The above stacktrace made me suspicious, and I instrumented the insertProviderAt(), addProvider(), and removeProvider() methods of java.security.Security to dump the stack of the calling thread. My suspicion was confirmed: It turned out that the Jruby code that was bundled with the web application registered its own provider (named BC) for cryptographic (in particular, MessageDigest) services, but never unregistered it during undeployment!

    The following stack trace shows how the provider was registered by Jruby:

    
      at java.lang.Thread.dumpStack(Thread.java:1158)
      at java.security.Security.insertProviderAt(Security.java:329)
      at org.jruby.RubyDigest.createDigest(RubyDigest.java:50)
      at org.jruby.libraries.DigestLibrary.load(DigestLibrary.java:40)
      at org.jruby.runtime.load.LoadService.smartLoad(LoadService.java:277)
      at org.jruby.runtime.load.LoadService.require(LoadService.java:299)
      at org.jruby.RubyDigest.createDigestSHA2(RubyDigest.java:104)
      at org.jruby.libraries.DigestLibrary$SHA2.load(DigestLibrary.java:63)
      at org.jruby.runtime.load.LoadService.smartLoad(LoadService.java:277)
      at org.jruby.runtime.load.LoadService.require(LoadService.java:299)
      at org.jruby.RubyKernel.require(RubyKernel.java:670)
      at org.jruby.RubyKernelInvokerSrequire1.call(Unknown Source)
      at org.jruby.runtime.callback.InvocationCallback.execute(InvocationCallback.java:49)
      at org.jruby.internal.runtime.methods.FullFunctionCallbackMethod.internalCall(FullFunctionCallbackMethod.java:76)
      at org.jruby.internal.runtime.methods.DynamicMethod.call(DynamicMethod.java:69)
      at org.jruby.RubyObject.callMethod(RubyObject.java:487)
      at org.jruby.RubyObject.callMethod(RubyObject.java:395)
      at org.jruby.evaluator.EvaluationState.evalInternal(EvaluationState.java:770)
      at org.jruby.evaluator.EvaluationState.evalInternal(EvaluationState.java:298)
      at org.jruby.evaluator.EvaluationState.eval(EvaluationState.java:163)
      at org.jruby.evaluator.EvaluationState.evalInternal(EvaluationState.java:1317)
      at org.jruby.evaluator.EvaluationState.eval(EvaluationState.java:163)
      at org.jruby.RubyObject.eval(RubyObject.java:563)
      ....
    

    but this provider was never unregistered.

    You may wonder why the serialization of application descriptors during subsequent deployments would pick up a MessageDigest algorithm implementation from the cryptographic provider that was registered by Jruby, when the Jruby-based web application had already been undeployed, and why any attempt to acquire such a MessageDigest implementation would result in a ThreadDeath error.

    Here's an explanation: Jruby inserted its cryptographic provider at position 2 (using java.security.Security.insertProviderAt()), thereby preempting the default cryptographic provider for MessageDigest (and other algorithms) named sun.security.provider.Sun, which ships with Sun's JRE and which, by default, is registered in 2nd position, i.e., takes precedence over any providers registered with a higher position number (the lower the position number, the higher the priority during cryptographic algorithm implementation lookups for which no provider name was specified). (The Java runtime's list of cryptographic service providers and their precedence order is initialized from the runtime's lib/security/java.security file resource. Any changes to this list, which require appropriate security permissions, have VM-wide visibility.)

    Now, this would not have been an issue had Jruby unregistered its provider during the undeployment of its bundling web application. But it never did, causing the application descriptor serialization during the subsequent redeployment to acquire a MessageDigest algorithm implementation whose underlying classloader (an instance of org.apache.catalina.loader.WebappClassLoader) had been deactivated when the Jruby-based web application was undeployed. And was does a deactivated org.apache.catalina.loader.WebappClassLoader instance do when it is being asked to load a resource? It throws a ThreadDeath error!

    This also explains why the Jruby-based web application was unusable following its redeployment: The Jruby code ended up getting the same BC provider and its MessageDigest implementation that had been registered and loaded during the initial deployment, and whose classloader had been deactivated during undeployment, so any attempt to perform any digest operations by Jruby started failing in the redeployed version of the bundling web application.

    Lessons Learned:

    • Jruby should have unregistered its cryptographic provider during undeployment, by calling java.security.Security.removeProvider("BC"). This could have been done during the invocation of org.jruby.webapp.AbstractRailsServlet.destroy(). In addition, Jruby should not have registered its provider with such a high priority (using java.security.Security.insertProviderAt()). Instead, it should have registered its provider using java.security.Security.addProvider(), which inserts the provider at the next available position, which normally is at the end of the list, and should have requested its provider's MessageDigest algorithm implementation by passing its provider's name to the java.security.MessageDigest.getInstance() method.

    • The web container should have been more resilient against the case where a web application registers a cryptographic provider, but then forgets to unregister it during undeployment. In other words, the web container should have cleaned up after the Jruby-based web application (or any other web application for that matter) during its undeployment, by unregistering any of its cryptographic providers that were left behind. This can be accomplished by the following code:
      
          import java.security.*;
      
          Provider[] providers = Security.getProviders();
          if (providers != null) {
              for (Provider provider : providers) {
                  if (provider.getClass().getClassLoader() == this) {
                      Security.removeProvider(provider.getName());
                  }
              }
          }
      

      which has been added to org.apache.catalina.loader.WebappClassLoader.stop() and committed to GlassFish v2 Build 44.

Wednesday Mar 21, 2007

Alternate Docroots in Web Applications

Alternate Docroots in Web Applications

Alternate Docroots in Web Applications

    GlassFish v2 Build 40 adds support for alternate docroots to web applications. Previously, support for alternate docroots was limited to virtual servers.

    Alternate docroots allow web applications to serve requests for certain resources from outside their own docroot, based on whether those requests match one (or more) of the URI patterns of the web application's alternate docroots.

    This means that web applications no longer need to bundle all their resources. Instead, part of their resources may be outsourced into server directories outside the web application's docroot, so-called alternate docroots, where they may be shared with other web applications.

    If a request matches an alternate docroot's URI pattern, it will be mapped to the alternate docroot by appending the request URI (minus the web application's context root!) to the alternate docroot's physical location (directory).

    The URI patterns of alternate docroots of web applications support exact, path prefix, and extension matches, just like their counterparts for virtual servers. If a request matches multiple URI patterns, the alternate docroot is determined according to the following precedence order:

    1. Exact match
    2. Longest path match
    3. Extension match

    Alternate docroots of web applications are configured as sun-web.xml properties, using the same syntax as alternate docroots for virtual servers.

    Example: The following sun-web.xml specifies 3 alternate docroots:

    
    <sun-web-app>
      <property name="alternatedocroot_1" value="from=/my.jpg dir=/srv/images/jpg"/>
      <property name="alternatedocroot_2" value="from=*.jpg dir=/srv/images/jpg"/>
      <property name="alternatedocroot_3" value="from=/jpg/* dir=/src/images"/>
    </sun-web-app>
    

    The value of each alternate docroot has two name-value components: The first component (with name from) specifies the alternate docroot's URI pattern as its value, and the second component (with name dir) specifies the alternate docroot's physical location (directory) as its value.

    In this example, the URI pattern of the first alternate docroot uses an exact match, whereas the URI patterns of the 2nd and 3rd alternate docroots use extension and path prefix matches, respectively.

    Assume the above sun-web.xml belongs to a web application deployed at http://<host>:<port>/myapp.

    The first alternate docroot will cause any requests with this URL:

    
      http://<host>:<port>/myapp/my.jpg
    
    to be mapped to this resource:
    
      /svr/images/jpg/my.jpg
    

    while the 2nd alternate docroot will cause any requests with a *.jpg suffix, as in:

    
      http://<host>:<port>/myapp/*.jpg
    

    to be served from this physical location:

    
      /svr/images/jpg
    

    whereas the 3rd alternate docroot will cause any requests whose URI starts with /myapp/jpg/, as in:

    
      http://<host>:<port>/myapp/jpg/*
    

    to be served from the same directory as the 2nd alternate docroot.

    For example, the 2nd alternate docroot maps this request:

    
      http://<host>:<port>/myapp/abc/def/my.jpg
    

    to:

    
      /srv/images/jpg/abc/def/my.jpg
    

    and the 3rd alternate docroot maps:

    
      http://<host>:<port>/myapp/jpg/abc/resource
    

    to:

    
      /srv/images/jpg/abc/resource
    

    If a request does not match any of the target web application's alternate docroots, or if the target web application does not specify any alternate docroots, the request will be served from the web application's standard docroot, as usual.

Friday Mar 09, 2007

Access Log Patterns and Nicknames

Access Log Patterns and Nicknames

Access Log Patterns and Nicknames

    GlassFish v2 makes it easier to specify well-known access log patterns, by allowing you to specify access log patterns by their well-established nicknames.

    In GlassFish, you normally use the format attribute of the <access-log> subelement of <http-service> to specify the access log pattern of your virtual servers.

    The default access log pattern looks like this:

    
      format="%client.name% %auth-user-name% %datetime% %request% %status% %response.length%"
    
    

    The above are just a subset of the access log pattern tokens supported by GlassFish. The complete list of access log pattern tokens, from which you can assemble your own custom access log pattern, is as follows:

    
      %auth-user-name%
      %client.dns%
      %client.name%
      %cookie.value%
      %datetime%
      %header.accept%
      %header.%
      %header.auth%
      %header.date%
      %header.if-mod-since%
      %header.user-agent%
      %header.referer%
      %http-method%
      %http-uri%
      %http-version%
      %query-str%
      %referer%
      %request%
      %response.length%
      %status%
      %user.agent%
      %vs.id%
    
    

    Due to popular demand, GlassFish v2 supports shortcuts for well known access log patterns, allowing you to specify an access log pattern by its nickname, instead of having to list its constituent tokens.

    Currently, the following access log pattern nicknames are supported:

    Additional nicknames will be added in the future, based on the GlassFish community's input.

    So, in order to change the access log pattern from the GlassFish default to Apache's "common" format, all you need to do is issue the following asadmin command (or use the admin gui):

    
      asadmin set server.http-service.access-log.format="common"
    
    
    or to change it to Apache's "combined" format, issue this command:
    
      asadmin set server.http-service.access-log.format="combined"
    
    
    Provided that access logging has been enabled, the above commands will take effect immediately, without requiring a server restart.

Thursday Mar 08, 2007

Presentation on Virtual Hosting Features in GlassFish

Presentation on Virtual Hosting Features in GlassFish

Presentation on Virtual Hosting Features in GlassFish

Please see the slides of my presentation I gave at the GlassFish User Experience meeting on 03/07/2007, which covers the info from my related blog, plus recent updates.

Wednesday Dec 13, 2006

Virtual Hosting Features in GlassFish

Virtual Hosting Features in GlassFish

Virtual Hosting Features in GlassFish

  1. Introduction
  2. This document describes the virtual hosting features available in GlassFish v2.

    Virtual hosting refers to the ability to run several web sites (domains) from a single physical server machine with a single IP address. Each web site is identified by its domain (host) name. HTTP requests are routed to the appropriate domain based on their host name. In order for this name-based form of virtual hosting (also known as shared IP hosting) to work, the DNS must be configured in such a way that each hosted domain name resolves to the server's IP address. Virtual hosting enables ISP/ASP business models.

  3. Isolation levels
  4. In name-based virtual hosting, virtual servers are isolated by their host (domain) names. Each virtual server is assigned a unique host (domain) name, and may also be assigned one or more alias names. The web container will route a request to a virtual server based on the host name in the request URL.

    GlassFish provides one additional level of isolation between virtual servers: at the HTTP port level, which allows virtual servers to be assigned HTTP ports for their exclusive use.

    In GlassFish, it is possible to associate a virtual server with only a subset of the configured HTTP listeners: A virtual server will receive only those requests that were received on any of the HTTP listeners listed in its http-listeners attribute. As a result of this, virtual servers, or groups of them, may be isolated from each other at the HTTP port level by configuring their http-listeners attributes with mutually exclusive sets of HTTP listeners.

    Isolation at the HTTP port level is not supported by Tomcat, where a virtual server is always bound to all HTTP listeners that belong to the same <Service> unit as the <Engine> unit to which the virtual server belongs. (In Tomcat, a <Service> element represents the combination of one or more <Connector> components that share a single <Engine> component for processing incoming requests, where an <Engine> contains one or more <Host> children, each with one or more <Context> children.)

    Example:

    Consider the following request mapping table:

    Protocol

    Domain Name

    Port Number

    URL Prefix

    Target Virtual Server

    http

    mydomain.com

    1111

    http://mydomain.com:1111

    vs-1

    https

    mydomain.com

    2222

    https://mydomain.com:2222

    vs-1

    http

    yourdomain.com

    3333

    http://yourdomain.com:3333

    vs-2

    https

    yourdomain.com

    4444

    https://yourdomain.com:4444

    vs-2



    The above request mapping requirements, which isolate virtual servers vs-1 and vs-2 at both the domain name and HTTP port levels, can be configured very easily in GlassFish, by leveraging the http-listeners attribute of <virtual-server>, as follows:

    
      <http-listener id="http-listener-1" port="1111"/>
    
      <http-listener id="http-listener-2" port="2222" security-enabled="true">
        <ssl cert-nickname="nickname-1"/>
      </http-listener>
    
      <http-listener id="http-listener-3" port="3333"/>
    
      <http-listener id="http-listener-4" port="4444" security-enabled="true">
        <ssl cert-nickname="nickname-2"/>
      </http-listener>
    
      <virtual-server id="vs-1" hosts="mydomain.com"
                      http-listeners="http-listener-1,http-listener-2"/>
    
      <virtual-server id="vs-2" hosts="yourdomain.com"
                      http-listeners="http-listener-3,http-listener-4"/>
    

  5. Root context configuration choices
  6. A virtual server's root context is defined as the context with the empty path (/). Any requests that cannot be mapped to any of the web modules deployed on a virtual server are mapped to the virtual server's root context.

    The physical location of a virtual server's root context is given by the virtual server's docroot property. Each virtual server may be configured with its own individual docroot.

    In GlassFish, it is possible to map a virtual server's root context to one of the virtual server's web modules, by adding a default-web-module attribute to the virtual server's <virtual-server> entry and having its value reference the selected web module.

    A virtual server's default web module "shadows" the virtual server's docroot, with the effect that any requests that cannot be mapped to any of the web modules deployed on the virtual server will now be mapped to the virtual server's default web module instead of the virtual server's docroot. In other words, a web module that has been designated as a virtual server's default web module will serve any requests that match its context path plus any requests that map to the virtual server's root context.

    A virtual server's docroot remains shadowed for as long as the virtual server declares a default web module. Once a virtual server's default-web-module attribute has been removed, the virtual server's docroot will be reinstantiated as the virtual server's root context.

    The default-web-module mechanism is a convenient way for having a single web module occupy both its designated context path as well as the virtual server's root context.

  7. Alternate docroots
  8. In GlassFish, a virtual server may specify alternate docroots in addition to its main docroot. Each alternate docroot is associated with one or more URI patterns. An alternate docroot is selected over the main docroot if the request matches one of its URI patterns. Both path (prefix) and extension matches are supported.

    For example, it is possible to configure a virtual server in such a way that any requests (with an empty context path) whose URI starts with /images/* will be mapped to one alternate docroot, whereas any requests (with an empty context path) whose URI ends in *.jsp will be mapped to a different alternate docroot. All other requests (with an empty context path) will be mapped to the virtual server's main docroot.

    If a request matches multiple alternate docroot URI patterns, the following precedence order is used:

    • Exact match
    • Longest path match
    • Extension match

    Alternate docroots allow virtual servers to share a subset of their docroot resources.

    See my blog for additional details and examples.

  9. Security
    1. Single Sign On
    2. Single Sign On (SSO) enables web applications to share client authentication state. SSO is enabled at the virtual server level (and configured as a virtual server property). With SSO, a user who successfully authenticates to one web application becomes implicitly logged in to any other web application (deployed on the same virtual server) that shares the same authentication realm.

    3. Security realm
    4. In many cases, it is useful to configure a security realm at the virtual server level, with the expectation that this security realm apply to all web modules deployed on the virtual server.

      GlassFish supports this requirement, by defining a property named authRealm at the <virtual-server> level, whose value must be the name of an authentication realm declared in domain.xml.

      Standalone web modules (that is, those not bundled in an EAR file) inherit the realm referenced by the virtual server on which they are deployed, unless they specify a realm name in their web.xml deployment descriptor, which takes precedence.

      See my blog for additional details.

    5. Security credentials
    6. GlassFish allows HTTP(S) listeners to be assigned to a virtual server (or group of virtual servers) for its (their) exclusive use (see the earlier section that talks about isolation levels). Ideally, the HTTPS listeners assigned to one group of virtual servers will have their security credentials (i.e., private keys and supporting certificate chains) separated from the security credentials of a different set of HTTPS listeners serving a different group of virtual servers.

      GlassFish currently supports only a single keystore per server instance, which means the security credentials of all of the instance's HTTPS listeners must be stored in the same keystore, and may be differentiated by an alias name (cert-nickname) only. There has been an enhancement filed against GlassFish (see Issue 657) to support multiple key- and truststores per server instance.

  10. Dynamic reconfiguration
  11. Virtual servers, including their attributes and properties, are dynamically reconfigurable, that is, the creation or deletion of a virtual server, or any changes to its attributes or properties, do not require a server restart in order to take effect.

Wednesday Nov 29, 2006

New Support for Virtual Server Realms

New Support for Virtual Server Realms

New Support for Virtual Server Realms

In an ISP/ASP environment, it is often useful to be able to configure a security realm at the virtual server level, with the expectation that this security realm apply to all web applications deployed on the virtual server.

GlassFish v2-b26 supports this requirement, by introducing a new property named "authRealm" at the <virtual-server> level, whose value must be the name of an authentication realm declared in domain.xml.

Standalone web applications (that is, those not bundled in an EAR file) now inherit the realm referenced by the "authRealm" property (if any) of the virtual server on which they are deployed, unless they specify a realm in their web.xml deployment descriptor, which takes precedence.

Thursday Oct 05, 2006

New Support for Virtual Directory Mappings

New Support for Virtual Directory Mappings

New Support for Virtual Directory Mappings

In GlassFish, every virtual server has a docroot property, which points to the local directory that will be used to serve resources for requests that cannot be mapped to any of the web contexts deployed on the virtual server.

Alternatively, a virtual server may designate one of its deployed web modules as its default web module, by specifying a default-web-module attribute whose value refers to one of the deployed web modules by its name. In this case, any requests that cannot be mapped to any of the web modules deployed on the virtual server will be mapped to the virtual server's default web module.

By default, a virtual server's docroot points to the ${com.sun.aas.instanceRoot}/docroot directory, as can be seen from this domain.xml snippet:

  <virtual-server id="server" [...]>
    <property name="docroot" value="${com.sun.aas.instanceRoot}/docroot"/>
  </virtual-server>

This means that when you access this URL:

  http://<host>:<port>/index.html
the contents of the ${com.sun.aas.instanceRoot}/docroot/index.html resource will be served.

By default, all virtual servers share the same docroot directory, but since the docroot property is configurable, each virtual server may be configured with its own, individual docroot.

As of GlassFish v2-b20, it is possible to configure a virtual server's docroot with finer granularity, based on the URL pattern of the request.

For example, it is now possible to configure a virtual server so that all requests with a URI that starts with /images/* will be served from one directory, whereas all requests whose URI ends in *.jsp will be served from a different directory. Each mapping from URL pattern to directory path constitutes an alternate docroot.

In GlassFish v2, a virtual server's alternate docroots are configured using the <property> subelement of <virtual-server> (this is mainly for historical reasons), as in the following sample domain.xml snippet:

  <virtual-server id="server" [...]>
    <property name="alternatedocroot_1" value="from=/images/* dir=/usr/gifs"/>
    <property name="alternatedocroot_2" value="from=*.jpg dir=/usr/gifs"/>
    <property name="alternatedocroot_3" value="from=*.jsp dir=/usr/jsps"/>
    <property name="docroot" value="${com.sun.aas.instanceRoot}/docroot"/>
  </virtual-server>

A future version of GlassFish will add proper configuration support for alternate docroots by defining a <virtual-directory-mapping> element under <virtual-server> in domain.xml, with <local-path> and <url-pattern> subelements. The above configuration, while still supported, could then be represented more cleanly, as follows:

  <virtual-server id="server" [...]>
    <virtual-directory-mapping>
      <local-path>/usr/gifs</local-path>
      <url-pattern>/images/*</url-pattern>
      <url-pattern>*.jpg</url-pattern>
    </virtual-directory-mapping>
    <virtual-directory-mapping>
      <local-path>/usr/jsps</local-path>
      <url-pattern>*.jsp</url-pattern>
    </virtual-directory-mapping>
    <property name="docroot" value="${com.sun.aas.instanceRoot}/docroot"/>
  </virtual-server>

If a request matches multiple alternate docroot URL patterns, the following precedence order is used:

  1. Exact match
  2. Longest path match
  3. Extension match

Any request that cannot be mapped to any of the web modules deployed on the virtual server, and whose URI does not match any of the virtual server's alternate docroot URL patterns, will continue to be served from the directory indicated by the virtual server's docroot property.

You may use the GlassFish admingui or command-line interface (asadmin) to add alternatedocroot_ properties to a virtual server. Please note that GlassFish does not support multi-valued properties in domain.xml. Therefore, if you want to specify multiple alternate docroots for a given virtual server, you need to make sure that each alternate docroot has a distinct property name that starts with alternatedocroot_.

A future enhancement will be to support alternate docroots for web modules. This will, amongst other things, allow web modules to share certain kinds of resources available on the server, instead of requiring every web module to bundle such resources. For example, by each defining alternate docroots with URI patterns of the form /images/* and *.jpg and a target directory of /usr/gifs, web modules would be able to share any *.jpg resources available under /usr/gifs (provided they have read access to this directory).

Monday Aug 14, 2006

Jetty has integrated the JSP compiler from GlassFish

Jetty has integrated the JSP compiler from GlassFish

Jetty has integrated the JSP compiler from GlassFish

The folks at Jetty report that they have switched to leveraging the JSP compiler from GlassFish and are about to release with it in the next couple of days.

They mention the quality and stability of the GlassFish code, the support they've received from the GlassFish community during their transition, as well as the receptiveness of the GlassFish community to new ideas and contributions, as the main reasons for switching. See Jan Bartel's blog for details.

Congratulations to the Jetty folks, and welcome on board!

Friday Aug 11, 2006

Greater flexibility in configuring your web application's class path

Greater flexibility in configuring your web application's class path

Greater flexibility in configuring your web application's class path

If you've ever had to augment your web application's classpath in the past, you're probably familiar with the extra-class-path attribute of the class-loader element in sun-web.xml.

For those who aren't: This attribute takes a colon or semi-colon separated list of paths that are added to the list of repositories that your web application's classloader will consider when asked to load a particular resource (by default, a web application's classloader is required to consider only resources located in the WEB-INF/lib and WEB-INF/classes directories of a web application).

The documentation of the extra-class-path attribute has failed to specify how relative paths listed in this attribute's value are to be resolved. Up until recently, GlassFish has interpreted relative paths as relative to the application server's current directory, which is set to your domain's $INSTANCE_ROOT/config during startup. For example, a path of the form WEB-INF/lib/extra/extra.jar used to be resolved to $INSTANCE_ROOT/config/WEB-INF/lib/extra/extra.jar. Not very useful!

As of GlassFish Promoted Build v2 12, we've changed the interpretation of any relative paths listed in the value of the extra-class-path attribute so they are resolved relative to a web application's context root, which will serve developers' needs much better.

For example, if you want your web application's classloader to also consider resources in WEB-INF/lib/extra/extra.jar and WEB-INF/extra-classes bundled with your web application, you specify the extra-class-path attribute in your web application's sun-web.xml, as follows:


  <sun-web-app>
    <class-loader extra-class-path="WEB-INF/lib/extra/extra.jar:WEB-INF/extra-classes"/>
  </sun-web-app>

Tuesday Jul 11, 2006

How to enable Eclipse's JDT Compiler for JSP compilations in GlassFish

How to enable Eclipse's JDT Compiler for JSP compilations in GlassFish

How to enable Eclipse's JDT Compiler for JSP compilations in GlassFish

A repeated request heard on the GlassFish developer forums has been the ability to use the Eclipse Java Development Tools (JDT) Compiler (instead of the javac from Sun's SDK) for JSP compilations.

The Eclipse JDT Compiler is used as the default compiler in Tomcat 5.5, and included with Tomcat's binary distribution.

This blog lists the steps required to use the JDT Compiler for JSP compilataions in GlassFish. Starting with the upcoming GlassFish Promoted Build v2 10 (which will be available from the GlassFish download page), it will be possible to enable the JDT compiler from Eclipse for JSP compilations in GlassFish, by adding the following two JAR files to your ${GF_HOME}/lib directory:

  1. jasper-compiler-jdt.jar: This JAR file has been taken unmodified from a Tomcat 5.5 binary distribution.

  2. jasper-compiler-jdt-ext.jar: This JAR file contains a version of Tomcat's org.apache.jasper.compiler.JDTCompiler which has been recompiled against the com-sun-commons-logging.jar from GlassFish.

    This recompilation step has been necessary because Tomcat's org.apache.jasper.compiler.JDTCompiler extends org.apache.jasper.compiler.Compiler and accesses the log instance variable of its superclass. Notice that while the type of the log instance variable in Tomcat is org.apache.commons.logging.Log, it was changed to com.sun.org.apache.commons.logging.Log in GlassFish, for the reasons explained here.

Notice that when using the default ant/javac based compiler in GlassFish, the fork initialization parameter of the org.apache.jasper.servlet.JspServlet defined in your domain's default-web.xml is used to specify whether javac compilations are to be performed in process or forked to a separate process. By default, fork is set to TRUE, to avoid memory leak and file locking problems that have been observed with javac. The fork init parameter is ignored by the JDTCompiler, which always runs in process.

Calendar

Feeds

Search

Links

Navigation

Referers