Grouper UI Architecture

    This document is current as of the v1.2.0 release. 

    1. Introduction
    2. The Architecture and How It Matches the Requirements
        2.1. Core Technology
        2.2. Framework
        2.3. Internationalization
        2.4. Extensibility
        2.5. Separation of Core Grouper UI Code from Site-Specific Code
        2.6. Accessibility
    3. Implementation Details
        3.1. Grouper UI Out of the Box
        3.2. Grouper UI Tailored for the University of Bristol (UoB)
        3.3. Supporting an Additional Language
        3.4. Overloading in ResourceBundles
            3.4.1. How It Works
            3.4.2. Multiple UIs One Instance
            3.4.3. Chaining ResourceBundles
        3.5. Overloading Struts Config Elements
        3.6. Additional Hooks to Extend the Grouper UI
        3.7. Page Composition Using Tiles
        3.8. Content Composition Using Tiles
        3.9. Dynamic Tiles
        3.10. JSP Tag Libraries

1. Introduction

This document describes the proposed architecture for the Grouper UI. The architecture aims to satisfy the following general requirements:

  1. Core technology
    Java-based web application to complement the Grouper API which is also Java-based
     
  2. Web application framework
    Use of established and well-documented framework not tied to particular platform
     
  3. Internationalization
    Designed to facilitate sites who wish to use a language other than english and in such a way that an institution can offer more than one language
     
  4. Extensibility
    Institutions will likely want to tailor the UI to reflect local business rules and available meta data
     
  5. Separation of core Grouper UI code from site-specific code
    Separation is important to ensure that local changes do not require modification of Grouper sources thus making upgrades much easier
     
  6. Accessibility
    Content must be accessible through use of DOM /CSS layouts rather than table layouts

2. The Architecture and How It Matches the Requirements

    ... Where relevant links are provided to some of the concepts and their justifications.

2.1. Core Technology

The Grouper UI will use the standard Servlet API (Version 2.3) which includes Java Server Pages (JSP). The servlet API is supported by many J2EE application servers, e.g., BEA Weblogic, IBM WebSphere and Sun Java System Application Server, as well as some open source application servers (which may not provide complete J2EE support) such as Resin and Apache Tomcat.

The UI will be developed using J2SE 1.4 and Tomcat 4.1.x. Given the widespread support for the Servlet API it is expected that the UI distribution will work with any compliant application server, however, we will depend on early adopters to feedback any issues.

2.2. Framework

Apache Struts has been chosen as the web application framework due to its wide acceptance and use in this arena. It is an implementation of the Model-View-Controller (MVC) design pattern which encourages separation of business logic from the presentation layer. This document will also show how the Struts framework can help satisfy other requirements indicated in the Introduction.

Struts supports a templating engine called Tiles which allows common UI elements to be defined once and re-used as often as needed.

2.3. Internationalization

Struts supports internationalization through the use of message resources and locale specific tiles (page 48). Since Struts was developed, the Java Standard Tag Library(JSTL) has been released, and this offers an alternative way of specifying message resource. The JSTL method will be adopted for this project.

2.4. Extensibility

Struts is configured through XML descriptors. By adding configuration elements to the XML descriptor, new actions can be created, or existing ones modified. The use of Tiles offers a great deal of freedom to redefine individual templates as deemed necessary.

2.5. Separation of Core Grouper UI Code from Site-Specific Code

Struts supports multiple configuration files. If two configuration elements with the same name are loaded the one loaded last is used. This allows new configuration elements to be added and existing ones to be overridden without actually modifying the Struts configuration file supplied in the Grouper UI distribution.

The same is true of Tiles definition descriptors.

Text for each additional language is contained in a separate properties file. Java ResourceBundlesautomatically look in the correct file based on the currently specified locale.

The Grouper UI will include a cascading style sheet (CSS) file which governs the visual appearance of the UI. We will provide a way of importing additional site-specified CSS files which can add additional style definitions or override existing definitions.

2.6. Accessibility

The Grouper UI will be tested for compliance.

3. Implementation Details

This section will discuss important features of the directory structure and configuration files used by the default Grouper UI distribution, and how institutions can tailor the UI to meet their own requirements. It focuses on the deployed directory structure. A working knowledge of Struts* will aid understanding.

*A few minor changes to some Struts source code has been required to make everything work as described below.

3.1. Grouper UI Out-of-the-Box

grouper                        

The web application document root.

           grouper

Grouper specific stylesheets

                      images

Grouper specific images

           i2mi

Internet 2 specific stylesheets

                      images

Internet 2 specific images

          WEB-INF

Web application data which cannot be accessed directly by a web browser. Includes the web.xml descriptor which defines the web application. The Struts ActionServlet is configured here.

                      classes

Java classes and resources placed here are available to the web application through its classloader. Grouper UI code will go here

                       jsp

Java Server Pages placed here cannot be referenced directly from a web browser / client, however, they can be accessed by servlets within this web application. For consistency all pages are processed by the Struts ActionServlet.

                       lib

.jar files placed here are available to the web application through its classloader. Jar files for the Struts framework and the Grouper API would go here

The Struts ActionServlet is configured in the web.xml file thus:

<servlet>
        <servlet-name>action</servlet-name>
        <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
        <init-param>
        <param-name>config</param-name>
        <param-value>/WEB-INF/struts-config.xml</param-value>
        </init-param>
        <load-on-startup>2</load-on-startup>
        </servlet>
        <servlet-mapping>
        <servlet-name>action</servlet-name>
        <url-pattern>*.do</url-pattern>
 </servlet-mapping>

The <servlet-mapping> element causes any url ending in .do to be served by the Struts ActionServlet.

Note the config parameter. The ActionServlet initialization is driven by this file.

In order to use Tiles with Struts, Struts must be configured to load the Tiles plugin. In the struts-config.xml file:

<plug-in className="org.apache.struts.tiles.TilesPlugin">
        <set-property property="moduleAware" value="true"/>
        <set-property property="definitions-debug" value="0"/>
        <set-property property="definitions-parser-details" value="0"/>
        <set-property property="definitions-parser-validate" value="false"/>
        <set-property property="definitions-config" value="/WEB-INF/tiles-def.xml"/>
 </plug-in>

The Tiles plugin initialization is driven by /WEB-INF/tiles-def.xml.

All display text for the Grouper UI should be defined in a Java ResourceBundle. At its simplest a ResourceBundle may map to a single properties file e.g.

ResourceBundle bundle = ResourceBundle.getBundle("/resources/grouper/nav",locale);

could be satisfied by a single file, nav.properties, in the WEB-INF/classes/resources/grouper.

The file contents would be something like:

groups.my=My Groups
groups.manage=Manage Groups
groups.create=Create Groups
groups.join=Join Groups

groups.edit.name=Name
groups.edit.description=Description
groups.edit.type=Type

and text would be rendered in JSP pages by code such as:

<fmt:message bundle="${nav}" key="groups.my"/>

or

<html:submit property="submit.save" value="${navMap\['stems.action.save'\]}"/>

This assumes that nav and navMap have been made available as JSTL variables. The Grouper UI code is responsible for setting them up in each users' session.
In this case, no matter what the locale, each user would get the same text, the default Grouper UI text, however, as discussed in the next section it is relatively straightforward to provide a new language, or even a variation on the default language e.g., British English as opposed to US English.

3.2. Grouper UI Tailored for the University of Bristol (UoB)

GroupsManager                

The web application document root. Note that we can name the web application as we see fit

              grouper

Grouper specific stylesheets / JavaScript

                            images

Grouper specific images

              i2mi

Internet 2 specific stylesheets / JavaScript

                            images

Internet 2 specific images

              uob

UoB specific stylesheets / JavaScript

                            images

UoB specific images

             WEB-INF

The Struts ActionServlet configuration must be modified.

                            classes

Additional resources / Java classes required by UoB copied here

                            jsp

Additional JSP files placed here - probably in a UoB subdirectory

                            lib

Any additional JAR files required by UoB code placed here

The Struts ActionServlet is configured in the web.xml file thus:

<servlet>
        <servlet-name>action</servlet-name>
        <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
        <init-param>
        <param-name>config</param-name>
        <param-value>/WEB-INF/struts-config_uob.xml,/WEB-INF/struts-config.xml,/WEB-INF/struts-config_uob.xml</param-value>
        </init-param>
        <init-param>
        <param-name>config/i2mi</param-name>
        <param-value>/WEB-INF/struts-config.xml</param-value>
        </init-param>
        <load-on-startup>2</load-on-startup> </servlet>

Note the config/i2mi parameter. This is only necessary IF you want to be able to run the unmodified Grouper UI at the same time as your modified version. This may be useful during development. In this instance i2mi represents a Struts module. We always use a module, however, we used the default module previously.

The config element now takes advantage of Struts' ability to load more than one config file. Ignore the first /WEB-INF/struts-config_uob.xml and focus on /WEB-INF/struts-config.xml, /WEB-INF/struts-config_uob.xml. Here we inherit all of Grouper's Struts configuration, loading any additional / replacement configuration we need. We might add some additional Struts actions, modify a form bean etc.

The /WEB-INF/struts-config_uob.xml file has a modified plug-in section:

<plug-in className="org.apache.struts.tiles.TilesPlugin">
        <set-property property="moduleAware" value="true"/>
        <set-property property="definitions-debug" value="0"/>
        <set-property property="definitions-parser-details" value="0"/>
        <set-property property="definitions-parser-validate" value="false"/>
        <set-property property="definitions-config" value="/WEB-INF/tiles-def.xml,/WEB-INF/tiles-def_uob.xml"/>
 </plug-in>

Again, all the Grouper Tiles configuration is inherited, whilst additional / replacement tiles can be loaded.

The reason for the additional /WEB-INF/struts-config_uob.xml in the config parameter above is that although Struts can load multiple configuration files, the first instance of a particular plugin wins. Without the additional /WEB-INF/struts-config_uob.xml, the Tiles plugin configured for Grouper alone would be loaded.

It is possible that although the default language for Grouper is English, that UoB would like to reword / rebrand parts pf the Grouper UI. We have already seen how text is stored in /WEB-INF/classes/resources/grouper/nav.properties. By adding a /WEB-INF/classes/resources/uob/nav.properties file we can configure the web application to first look at the UoB bundle. If it fails to find a particular key it will default to the Grouper bundle.*

In the same way that readable text is rendered on a page based upon looking up a key and obtaining a value, it is also possible to define urls for images and style sheets in a ResourceBundle e.g., /WEB-INF/classes/resources/grouper/media.properties: image.organisation-logo=grouper/images/organisation-logo.jpg
image.grouper-logo=grouper/images/grouper.jpg
/WEB-INF/classes/resources/uob/media.properties might include:
image.organisation-logo=uob/images/banner.logo.gif
css.additional=uob/uob.css
*see 3.4. for a discussion of the alternative approaches to overloading properties for locales and application components.

3.3. Supporting an Additional Language

Let's say that Bristol had a requirement to have a Spanish language version of the Grouper UI. The following list explains how this might be achieved:

  1. Copy /WEB-INF/classes/resources/uob/nav.properties to /WEB-INF/classes/resources/uob/nav_es.properties, and replace the English text with Spanish text - leaving the keys alone.
  2. Copy /WEB-INF/classes/resources/uob/media.properties to /WEB-INF/classes/resources/uob/media_es.properties, and replace the paths to any images which include English text to paths to new images which incorporate Spanish text - leaving the keys alone.
  3. Create a file /WEB-INF/tiles-def_uob_es.xml and add any definitions required.
  4. Provide the means to select a language*.

* If a user's browser is configured with a locale it might automatically be selected, however, it may be necessary to incorporate a selection means on the initial page of the site - this might be driven from a configuration option for the Grouper UI, e.g.,:

    known.locales=en,es

Java Locale objects can, in principle, return an appropriate display name for a locale.

3.4. Overloading in ResourceBundles

3.4.1. How it Works

A ResourceBundle is created based on a specific Locale. A Java Locale can be made up from 3 components:

  1. Language code
  2. Country code
  3. Variant

A Locale's string representation would be: language_country_variant. All the properties files associated with a ResourceBundle for a given Locale can be calculated by starting with the base name and adding _ followed by the Locale string,e.g.,:

    nav_en_GB -> british english. If a key is not found here check nav_en. If a key is not found here checknav.

In our scenario nav provides the default Grouper UI text. A US university could add / replace key values by creating anav_en_US properties file where as Bristol would go withnav_en_GB.

If Bristol also wanted to provide for a local dialect - 'west country' they could create a file nav_en_GB_WestCountry.properties.

At this point it isn't possible to extend the naming system. The java.util.Locale documentation discusses multiple variants, however, the actual code does not deal with them.

Now consider a release of the Grouper UI which is provided with a 'contributed' nav_en_GB.properties file. Bristol might still want to make changes to the contributed text and could do so by having a bristol variant e.g. nav_en_GB_Bristol, however, it would then not be possible to have a 'south west' variant.

3.4.2. Multiple UIs One Instance

Consider a scenario where an institution wants to offer different versions of the UI i.e., a student interface and a staff interface, or a read-only Portal interface where XML is output rather than XHTML.

Also consider a service provider of a Grouper UI wanting to offer a central but customizable service e.g. a regional consortium who would provide UI services to Bath University, University of the West of England, Bristol University, Exeter university and Plymouth University.

Each variation could be run as a separately configured web application. An alternative would be for one instance to support multiple views. In Struts terms this would mean a separate module for each variation.

    In principle, each variation can be considered a Locale variant e.g. at Bristol we could use:

    By choosing the correct Locale appropriate text can be presented.

    In the consortium example:

    However, there is a limit to the hierarchy. nav_en_GB_uob_staff is not possible.

3.4.3. Chaining ResourceBundles

An alternative is to chain ResourceBundles i.e., look for a resource in an institution first. e.g. nav_uob If not found, look for a resource in the supplied Grouper ResourceBundle e.g., nav. Several ResourceBundles could be chained if necessary.

Chaining may lead to other problems e.g., if the Grouper UI was supplied with a contributed nav_es.properties, Bristol could still could overload this by having a nav_uob_es.properties, however, consider a key grouper.audit-trail:

If the Locale was set to es, grouper.audit-trail would returnlog. Therefore, any keys overridden in nav_uob_en_GB would need to be overridden in nav_uob_es as well.

An alternative would be to copy nav_es to nav_uob_es and then override anything using nav_uob_es_ES (or other South American country codes!)

3.5. Overloading Struts Config Elements

When loading multiple Struts config files, the latter of two elements identified by the same name wins, however, in some cases it may be useful to extend rather than replace a config element. One example would be ActionForms.The Grouper UI would provide an HTML form and a matching Struts ActionForm which would deal with known group types, however, institutions may define their own group types with attributes not known to the base Grouper UI code. An institution wishing to alter the ActionForm definition would, ideally, only specify the additional fields necessary rather than copy the definition and modify it.

This type of inheritance may be achieved by modifying the institutional Struts config file at build time. A system will be devised to make this automatic.

Struts Actionsnormally define a limited set of ActionForwards- destinations, one of which is chosen based on the Action logic. An institution may want to change the normal page flow by redefining a forward, or by providing extra logic to determine which ActionForward should be chosen.

Redefinition of an existing ActionForward could be achieved automatically at build time.

Providing extra logic could be achieved by various means e.g., redefine the Action class called to be a subclass of the Grouper UI supplied Action class, and call super.execute(...)

3.6. Additional Hooks to Extend the Grouper UI

Let's say that every time a group was created through the Grouper UI, Bristol wanted to immediately set up a shared mail folder for that group. This might be achieved by subclassing a Struts CreateGroupAction, however, a better way would probably be to register listeners for particular events.

We would need to decide if the UI code or the Grouper API is the appropriate place to register listeners for API calls which modify the underlying repository.

3.7. Page Composition Using Tiles

The actual page template for the Grouper UI has not yet been determined. The following discussion describes an approach to defining page layouts. Not all of the page areas defined in the following diagram may be used by the Grouper UI, however, individual institutions will be free to tailor the layout to meet their needs.

    Such a page can be represented using a Tiles definition:

<definition name="BaseDef" path="/WEB-INF/jsp/template.jsp">
        <put name="header"    type="definition" value="headerDef"/>
        <put name="footer"    type="definition" value="footerDef"/>
        <put name="subheader" type="definition" value="subheaderDef"/>
        <put name="content"   type="definition" value="/WEB-INF/jsp/empty.jsp"/>
        <put name="left"      type="definition" value="leftDef"/>
        <put name="right"     type="definition" value="rightDef"/>
        <put name="head"      type="definition" value="headDef"/>
        <put name="message"   type="definition" value="messageDef"/>
        <put name="init" type="definition" value="initDef"/>
  </definition>

Each xxxDef is a reference to another definition where, typically, the path will be defined as a specific JSP file.

Other page definitions can extend the BaseDef e.g.,:

<definition extends="BaseDef" name="EditGroupDef">
        <put name="content" type="page" value="/WEB-INF/jsp/EditGroup.jsp"/>
 </definition>

This page has exactly the same layout as the BaseDef, however, the content attribute has been overridden. Any number of attributes could be overridden e.g.,:

<definition extends="BaseDef" name="EditGroupDef">
        <put name="content" type="page" value="/WEB-INF/jsp/EditGroup.jsp"/>
        <put name="left"    type="page" value="/WEB-INF/jsp/EditGroupLeft.jsp"/>
  </definition>

In the distributed Grouper UI it is likely that areas such asheader and footer will be static, whilstsubheader, left and right will be dynamically generated based on context. Each actual page displayed to a user will override content. Institutions are free to compose pages as they see fit.

BaseDef is implemented through /WEB-INF/jsp/template.jsp:

<%@include file="/WEB-INF/jsp/include.jsp"%>
  <tiles:insert attribute="init" />
  <html:html locale="true">
  <head>
        <tiles:insert attribute="head"/>
  </head>
  <body>
        <div id="Header">
             <tiles:insert attribute="header" />
        </div>
        <div id="Navbar">
             <tiles:insert attribute='subheader'/>
        </div>
        <div id="Sidebar">
             <tiles:insert attribute="left" />
        </div>
        <div id="ContentSpace">
             <div id="TitleBox">
                  <tiles:insert attribute="title" />
             </div>
             <c:if test="${\!empty message}">
                  <div id="Message">
                       <tiles:insert attribute="message" />
                  </div>
             </c:if>
             <\!--content-->
             <div id="Content">
                  <tiles:insert attribute='content' />
             </div>
             <\!--/content-->
       </div>
       <div id="Right">
           <tiles:insert attribute="right" />
       </div>
       <div id="Footer">
           <tiles:insert attribute="footer" />
       </div>
 </body>
 </html:html>

The initial include holds common Java import statements and taglib references - needed for tiles and html tags, amongst others, to be handled correctly.

Each tiles:insert tag is replaced by the result of loading the relevant Tiles definition indicated by the attribute e.g.header is replaced by headerDef which is defined as having a path of /WEB-INF/jsp/header.jsp

It is entirely possible to have several different templates. It is also straightforward to override the definition of BaseDef such that a JSP file other than template.jsp acts as the common page template. This is a very powerful approach since changing a few files can completely alter how pages are composed.

Of course, if existing page elements are rearranged it is likely that CSS definitions will also need to be overridden.

3.8. Content Composition Using Tiles

The previous section dealt with the structural composition of a page from a coarse-grained perspective. It is also inevitable that institutions will want to customize what happens in the content area e.g. when browsing groups, rather than just display the displayExtension, other custom attributes may be shown.

The same approach as the last section can be applied, if a content area is composed of several tiles. Taking the Manage Groups page as an example:

The content is generated from ManageGroups.jsp. Box 0 is a tile (browseStems.jsp). The rest of the content is not yet broken down further, but boxes 1 through 6 show how further tiles could be used to assemble the content.

  1. Bread crumb trail showing where you are in the hierarchy
  2. The child stems and groups for the current location in the hierarchy (not actually used in the example below)
  3. A tile which displays a stem
  4. A tile which displays a group
  5. A tile for displaying a Search groups form.
  6. A tile for showing what actions are available for the current stem

    All of these tiles could be re-used elsewhere. In addition they can be parameterized in exactly the same way as BaseDef in the previous section. e.g.,
    <definition name="browseStemsDef"
                     path="/WEB-INF/jsp/browseStems.jsp"
                     controllerUrl="/prepareStems.do">
             <put name="breadcrumb" type="page" value="breadcrumb.jsp"/>
             <put name="childStem" type="page" value="childStem.jsp"/>
             <put name="childGroup" type="page" value="childGroup.jsp"/>
     </definition>
    

    Note the controllerUrl. This is a Struts action which is responsible for making the relevant data structures available to the tile.

    The tile would be inserted into the content, thus:

<tiles:insert definition="browseStemsDef"/>

    or

<tiles:insert definition="browseStemsDef"
              controllerUrl="/uobPrepareStems.do">
    <put name="childGroup"
         type="page"
         value="uobChildGroup.jsp
</tiles:insert>

    In the former case all the defaults are used from the definition, whereas in the latter, controllerUrl and the childGroup definitions are overloaded.

3.9. Dynamic Tiles

Given that sites may define their own attributes for custom groups and for Subjects, as well as new Subject types, and given that depending on context it may be desirable to show a different view of an object, a flexible approach has been adopted for rendering objects. In essence, the Grouper UI can determine, at run time, the appropriate Tile to display based upon the type (and possibly subtype) of an object, and a named view. If a named view for an object has not been configured in an appropriate Java properties file, then a default view will be selected. The core Grouper UI will only configure a minimal set of default views for the object types and subtypes it knows about, however, institutions will be free to configure additional tiles to suit their needs.

A generic dynamic tile has been defined thus:

<definition controllerUrl="/getDynamicTileName.do" name="dynamicTileDef" path="/WEB-INF/jsp/dynamicTile.jsp"/>

The controllerUrl is responsible for determining the actual tile to load based on attributes assigned to the dynamic tile:
 

<tiles:insert definition="dynamicTileDef" flush="false">
        <tiles:put name="viewObject" beanName="subject"/>
        <tiles:put name="view" value="groupMember"/>
 </tiles:insert>

In this example, a Subject is being rendered as a member of a group. The default view for all subject types is to display the descriptionattribute of the Subject, however, it may be desirable to visually differentiate different Subject types and provide links appropriate to that Subject type. The dynamic tile approach provides this level of extensibility. Moreover, the implementation of the code which resolves an object and a view to a tile is pluggable i.e., can be extended or replaced completely, so that new object types / algorithms can be added to the Grouper UI.

3.10. JSP Tag Libraries

Where possible, the Java Standard Tag Library will be used in templates rather than scriptlets. Other open source tag libraries available from the Apache Jakarta site may be used. In addition, it is possible that we may write some custom tag libraries that work with Grouper objects.

It will be possible for institutions to configure and use additional tag libraries of their choice.