Taylor

Taylor Portal

From Taylor

Taylor Portal is a light-weight portal based on Ajax/Richfaces, Facelets, Seam, and JSF.

Why Taylor Portal?

The state of the art has progressed a long way since the Java Portlet spec was first introduced. The most notable improvements are Ajax and Dependency Injection (DI) frameworks. Thanks to these concepts we no longer need a heavy portal framework that only gets in the way.

  • Ajax provides partial page rendering
  • DI plus a few (optional) conventions provides plug-ability

So, instead of integrating these frameworks into a portal, let's just uses these frameworks as the basis for the portal.

There are 2 main pieces to Taylor Portal:

  • the master webapp war
  • and plug-ins.

Contents

Master Web App

Taylor Portal is just a typical JEE web application. The only difference is that most of the logic is delegated to plug-ins. You can think of the webapp is the main entry point (or portal) for accessing the functionality of the plug-ins (or portlets).

The webapp just provides the components needed for this launch pad:

  • master deployment descriptors
  • home page
  • log in page
  • menus
  • themes
  • dashboard
  • error page

Find more on the structure of the webapp here: Project Anatomy

The portal War is deployed in an Ear along with the plug-in Jars.

Themes

Portals have always had a Theme feature to customize the look and feel of the portal. Taylor Portal leverages Seam Themes and Facelets templates and tags to provide this feature.

Themes are comprised of templates, tags, css, and images. Taylor Portal comes with default themes that focus on several areas:

  • The Master Layout template is focused on headers, footers, and menus that are common to all pages.
  • The Dashboard provides a more traditional portal-like home page that is divided into Tabs, Columns, Rows, and Panels.
    • These are configured via meta-data as discussed below.
  • Page flow Wizard templates focus on user interactions that requires a series of steps.
  • Crud templates focus on user interactions that revolve around document viewing and editing and reference data administration.

You are not limited to these themes. You can create your own and let maven merge them with the taylor-portal-web war.

The Facelets that leverage these templates are packaged into plug-ins as discussed below.

Plug-ins

Plug-ins are analogous to portlets. However, portels largely just focus on integrating your code into the portal rendering lifecycle. Thanks to Ajax and Facelets this is a relatively trivial matter. Instead we can focus on providing functionality and let existing frameworks do the rest.

Plug-ins have 2 main pieces:

  • managed components (such as Seam components)
  • and Facelets fragments that leverage the templates and tags from the themes.

These pieces are packaged into a Jar and register the following with the portal on start up:

  • components
  • navigation rules
  • menus
  • bundles
  • panels

Federated Descriptors

Registration is controlled by federated deployment descriptor. This is nothing new. This capability is already provided by the various descriptors. By federated we just mean that each plug-in contains its own descriptors.

  • components are defined in META-INF/components.xml and annotations
  • navigation rules are defined in META-INF/faces-config.xml
  • pages are defines in META-INF/pages.xml
    • NOTE: this requires a Seam Extension found in taylor-commons that will eventually find its way into Seam proper

Also, the webapp contains a set of masters descriptors related to the war itself.

The content of these descriptors is totally within your control. The portal doesn't really care. The portal does provide several of its own components for registering menus, bundles, and panels.

Menu Configuration

Menus are the main way that you access your plug-ins from the portal. Menus can be configured in Java and Xml using a simple Seam component. On start up the components store the data in the TaylorDS for access and customization at runtime.

  • Top level menus are typically defined in components.xml
<components xmlns="http://jboss.com/products/seam/components"
	xmlns:menu="http://taylor.net/portal/menu"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://jboss.com/products/seam/components 
		http://jboss.com/products/seam/components-2.0.xsd">
	
	<menu:menu-init name="AdminMenu"
		label="#{messages['Admin_Menu']}"
		sortOrder="99"
		rendered="#{s:hasRole('Admin')}"
	/>
			
	<menu:menu-init name="UserMenu" 
		sortOrder="10"
		label="#{messages['User_Menu']}"
		rendered="#{s:hasRole('Admin')}"
		disabled=""
		action="findUser"
		conversationPropagation="end"
		parent="AdminMenu" 
	/>

	<menu:menu-init name="GroupMenu" 
		sortOrder="11"
		label="#{messages['Group_Menu']}"
		rendered="#{s:hasRole('Admin')}"
		disabled=""
		action="findGroup"
		conversationPropagation="end"
		parent="AdminMenu" 
	/>

</components>

  • Individual menus are typically defined in the entity finder beans
	/** @generated */
	@Name("MyEntityMenu")
	public static class MyEntityMenuInit extends MenuInit {

		/** @generated */
		public OptionMenuInit() {
			super("AMyTopMenu", 0l, 
				"#{messages['MyEntity_Menu']}",
				"#{s:hasRole('User')}", "", 
				"findMyEntity", "end");
		}		
	}
  • The layout-template dynamically generates the menus from the meta-data
	<c:forEach items="#{menubar.menus}" var="menu">
		<rich:dropDownMenu
			value="#{menu.label}"
			rendered="#{menu.rendered}"
			disabled="#{menu.disabled}">
			<c:forEach items="#{menu.children}" var="item">
				<rich:menuItem
					value="#{item.label}"
					rendered="#{item.rendered}"
					disabled="#{item.disabled}"
					action="#{item.action}">
					<s:conversationPropagation type="#{item.conversationPropagation}"/>
				</rich:menuItem>
			</c:forEach>
		</rich:dropDownMenu>
	</c:forEach>

Properties

  • order by sortOrder, name
  • label, rendered, and disabled use expression language to determine what to display and render

Bundle Configuration

Seam provides a component for accessing resource bundles. However, we want to federate the definition of the bundles. This component will add a plug-in's bundles to the master.

	<component name="myplugin.bundles" class="net.taylor.seam.ext.BundleNames" 
		bundleNames="myplugin-rules,myplugin-application,myplugin-messages" />

Taylor MDA will generate these bundles:

  • <project-name>-messages is generated from the model
  • <project-name>-application is generated blank to allow for customization of the generated <project-name>-messages
  • <project-name>-rules is generated blank to allow for customization of presentation rules


Forthcoming storage of bundles in TaylorDS

Dashboard Configuration

The dashboard is usually going to be your home page. It can be organized into a set of tabs with rows and columns. The rows and columns form cells which each contain a panel. The panel can be configured to span multiple columns and rows and have a width. So, along with a theme, you can organize this as you would expect a home page or dashboard to look.

Each panel referenced a Facelets fragment which is provided by a plug-in. The Facelets in turn reference the components needed to provide the content.

<components xmlns="http://jboss.com/products/seam/components"
	xmlns:dash="http://taylor.net/portal/dashboard"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://jboss.com/products/seam/components 
		http://jboss.com/products/seam/components-2.0.xsd">

	<dash:tab-init name="OverviewTab"
		label="Overview"
		sortOrder="0"
		columnCount="1"
		rendered="#{s:hasRole('User')}"
		disabled="">
		<panels>
			<value>#{Panel1}</value>
			<value>#{Panel2}</value>
		</panels>
	</dash:tab-init>
              
	<dash:panel-init name="Panel1"
		label="Panel1"
		sortOrder="0"
		rendered="#{s:hasRole('User')}"
		disabled=""
		colspan="1"
		rowspan="1"
		width="100%"
		facelet="/jsf/Panel1.xhtml"
	/>
              
	<dash:panel-init name="Panel2"
		label="Panel2"
		sortOrder="0"
		rendered="#{s:hasRole('User')}"
		disabled=""
		colspan="1"
		rowspan="1"
		width="50%"
		facelet="/jsf/Panel2.xhtml"
	/>
			
</components>
<rich:tabPanel switchType="ajax" headerAlignment="left">
	<c:forEach items="#{dashboard.tabs}" var="tab">
		<rich:tab name="#{tab.name}" label="#{tab.label}" id="#{tab.name}"
			rendered="#{tab.rendered}" disabled="#{tab.disabled}">
			<table cellpadding="10" cellspacing="10" width="100%">
				<c:forEach items="#{tab.rows}" var="row">
					<tr>
						<c:forEach items="#{row.panels}" var="panel">
							<td width="#{panel.width}" valign="top" colspan="#{panel.colspan}"
								rowspan="#{panel.rowspan}">
								<tt:toggle-panel rendered="true" switchType="client"
									id="#{panel.name}" opened="true"
									label="#{panel.label}">
									<ui:include src="#{panel.facelet}"></ui:include>
								</tt:toggle-panel>
							</td>
						</c:forEach>
					</tr>
				</c:forEach>
			</table>
		</rich:tab>
	</c:forEach>
</rich:tabPanel>

Forthcoming

  • user defined tabs
  • facelet init-params

Facelets

We also want to federate deployment of the Facelets to the plug-ins as well.

Facelets are packaged in a Jar and accessed from the classpath. This just requires a custom facelets resource resolver that is configured in the master web.xml.

Note that these are generally facelet fragments that gain most of their functionality from the templates and tags defined in a theme.

public class ClasspathResourceResolver extends DefaultResourceResolver
		implements ResourceResolver {
	@Override
	public URL resolveUrl(String resource) {
		URL resourceUrl = super.resolveUrl(resource);
		if (resourceUrl == null) {
			if (resource.startsWith("/")) {
				resource = resource.substring(1);
			}
			resourceUrl = Thread.currentThread().getContextClassLoader()
					.getResource(resource);
		}
		return resourceUrl;
	}
}

<context-param>
	<param-name>facelets.RESOURCE_RESOLVER</param-name>
	<param-value>net.taylor.jsf.ClasspathResourceResolver</param-value>
</context-param>

Data Sources

If your plugin uses JPA then you will need to define an entity manager in the components.xml per usual.

	<persistence:managed-persistence-context name="mypluginEntityManager"
		auto-create="true" persistence-unit-jndi-name="java:/myplugin"/>

And deploy your datasource descriptor to the deploy directory per usual.

  • myplugin-ds.xml

Integration

Portals are traditional about integration at the presentation layer. With Taylor Portal we can divide this integration into three categories:

Subsystem Plug-ins

This is just a way to divide your application into configurable units, instead of creating a monolithic application. You are in control of all the code. But you want to use Maven dependencies to build EARs for different configurations.

Generic Plug-ins

These are plug-ins that you deploy once and configure multiple times. Examples could be:

  • an IFrame panel which can be configured to point to a remove site
  • a Weather panel that is configured with a zip and calls a web service
  • a Chart panel that is configured with filters

Wrapper Plug-ins

These are plug-ins that you develop to integrate specific external systems. Examples could be:

  • an SVN panel that shows recent changes
  • a JIRA panel that shows recent changes

Data Extensibility

Portals typically do not handle data level extensibility. This is usually left up to EAI and ESB tools. But these tools focus on back-end integration and not on the presentation layer.

Here is a common scenario:

  1. You are building a new application that will replace several legacy applications.
  2. You will be decommissioning the legacy systems one by one over several phases.
  3. In the mean time, you will have to integrate with these legacy systems, but they require additional data that the new application does not require and you don't want to pollute your core model with these extra fields.
  4. Instead, you want to model these extra data items into legacy system specific plugins, which can be unplugged when the system is retired.
  5. These extra data items should extend the core presentation layer so that users can enter these items when and where required.

Taylor Portal provides an extensibility feature to extend the presentation layer. It consists of the following:

  • Metadata to define the mappings
  • Dynamic Folder Tree Node which extends the UI based on the metadata
  • A cross reference (Xref) entity and service for storing the data associations.

These are the pieces that you can configure:

  • Define entity mappings in your plugin's components.xml or its code
	<ext:entity-map name="projectCodeUserEntityMap"
		sourceClass="net.taylor.identity.entity.User"
		targetClass="com.acme.sampleadapter.entity.ProjectCode"
		cardinality="ManyToMany"
	/>
	@Startup
	@Name("projectCodeUserEntityMap")
	public static class ProjectCodeUserEntityMap extends EntityMap {
		public ProjectCodeUserEntityMap () {
			super(User.class, ProjectCode.class, Cardinality.ManyToMany, null, null, null);
		}
	}
  • Define label in sample-adapter-messages.properties
projectCodeUserEntityMap=Project Codes
  • Make your code extensible by extending the ExtensibleEntityTreeNode
    • Note, in the future this will either be generated or rolled into the base EntityTreeNode class
public class UserTreeNode extends ExtensibleEntityTreeNode<User> {
	...
}

Existing Plug-ins

  • taylor-portal (profile/preferences???)
  • taylor-audit
  • taylor-identity
  • taylor-bpm
  • taylor-search Forthcoming

Related

Personal tools