A skin refers to a user interface's appearance; it gives a Web application a different look and feel. A skin changes the way the user interface appears when a user clicks a button, but does not change the UI's behavior. A change in the skin thus results in a change to an application's appearance, but to achieve that modification, your Web application must know how to use a skin.
Why should you skin a Web application in the first place? Well, there are several motives for using skins, but certainly they are not always a must. In a simple application, skinning it would be overkill, but in some situations, as described in the list below, you must deal with skins:
- When the skin is a system requirement: When the user can select his own skin or even create his own.
- When you want to give skin capabilities to an enterprise component framework: If you create different solutions for different clients, you could reuse all your components (taglibs), if your components have skinning capabilities, by just changing each client's skin.
- When a different skin is needed according to a business scenario: For example, in a marketplace or multi-banking application, different entities are working in the same system and you need to brand the application according to the user's corporate image.
Skinning a Web application is not an easy task. You can use Cascading Style Sheets and change an image's path, but you are limited to what you can do with CSS. If you have a component that looks completely different in each skin, that is, if the HTML differs in each skin, CSS won't help you. However, you could use CSS if simply changing styles solves your problem.
A good approach to creating a skin is to determine each piece of the user interface and generalize these pieces to apply an appearance to each one. For example, if, in Skin A, you have a frame component that is just a plain table and, in Skin B, a more complex table with headers, footers, images, and even sounds, different HTML (more
<td> tages) should be generated for each skin's frame. As an example, let's suppose that in Skin A, the HTML that must be generated to render a label is:
<p>This is my Label</p>
Now, in Skin B, this is how a label would be rendered:
<table background="/images/tablebg.gif"> <tr> <td bgcolor="#0000FF"> </td> <td background="/images/cellbg.gif"> This is my Label </td> <td bgcolor="#0000FF"> </td> </tr> </table>
As you can see, these two pieces of UI differ completely in each skin. They both have the same information (
This is my Label), but are rendered with different HTML tags. This functionality couldn't be achieved with CSS alone. Perhaps using Extensible Stylesheet Language Transformations or XSL could be an option. Or you could use Xkins.
What is Xkins?
Xkins is a framework that manages skins for your Web application. In the early server-side Java days, you hard-coded HTML into a servlet. Then, JSP (JavaServer Pages) came along to allow you to put your HTML outside Java code. Nowadays, we have the same problem with taglibs that have HTML tags hard-coded in Java code. Using Xkins, you can place HTML outside your code with an additional and powerful feature: skins. For a detailed information about Xkins, visit Xkins's homepage.
Figure 1 illustrates Xkins's role in a Web application.
A Web application that uses Xkins and Struts through taglibs follows this request lifecycle:
- Struts initializes Xkins with the Xkins plug-in.
- Struts controller receives the HTTP request.
- Struts executes the process and forwards it to the JSP page view.
- The JSP page uses taglibs to render the page.
- The taglib uses Xkins through the Xkins facade:
XkinProcessorgets the user's skin and the template that the taglib commands to render.
TemplateProcessorassociated with the template.
TemplateProcessoris the class responsible for rendering the UI piece that composes the skin. The
TemplateProcessorcould use Velocity, JBYTE (Java By Template Engine), Groovy, or other template engine to render the output.
TemplateProcessoruses the resources from the skin (elements and paths) and returns the result of the template processing to the taglib.
- The taglib renders the result of the template processing to the Web browser.
Xkins addresses skin management by following these basic concepts:
- Keep all HTML generation out of Java code: Taglibs usually generate HTML code. Changing this code requires changing the Java code and redeploying the application. Xkins allows you to externalize HTML generation by placing HTML in definition files (XML files). In addition, Xkins allows you to keep plain HTML formatting tags out of JSP pages to further externalize the application's look and feel.
- Define a skin structure: Templates, resources, and paths compose a skin. Resources can be either constants or elements like images and CSS files. Defining paths helps you organize your skin files. Defining templates helps you reuse the pieces of the UI throughout your application.
- Allow extensions to the Xkins framework: You can extend Xkins to use your own template language for rendering according to your needs. If you need, for example, image generation, you can implement a template processor that takes a template and generates an image. Xkins comes with template processors based on Velocity and JBYTE. If you prefer Groovy, for instance, you could create a Groovy template processor to render your UI pieces.
- Split UI in basic elements: In Xkins, you can strip all the UI's pieces and create templates with them. In this way, you can reuse these pieces and change anything you need to make a skin look different.
- Use inheritance to minimize skin maintenance: In Xkins, a skin can extend other skins and use all templates, paths, and resources that its parent has. Thus, you reduce template maintenance.
- Use composition to create skins: In addition to inheritance, Xkins also uses composition to minimize maintenance and promote reuse of your templates. With this feature, users can create their own personalized skins from your application by selecting different pieces of the UI from existing skins.
- Define a skin type: Using a skin type, you can assure that all skins loaded in an Xkins instance have at least the same templates as the type. A skin type is the skin of which all other skins must extend to be valid in an Xkins instance. By Xkins instance, I mean a group of skins loaded together for use by the Web application.
One important benefit Xkins offers is that all HTML is in one place, and, if you need to tune it, you just simply change the templates. For instance, if your pages are too big, detect where the excessive HTML generation is or decide what images could be stripped, and then change the templates to reduce page size. You could also have a lightweight skin for those users accessing your Web application with low-speed connections and a richer skin UI for broadband users.
Note that you can use Xkins along with CSS. In fact, CSS use is recommended for font styles and colors, because reusing CSS classes prevents the need to explicitly indicate the font face each time, thus minimizing page size.
A skin can be encapsulated into a single file (zip file) for easy deployment in a Web application. If you define a skin type, third-party skins can be added to your Web application if they conform to the skin type you declare.
You can use Xkins in many ways, but using Xkins with taglibs offers the best approach in a Web application. You can use these tags to generate your pages or to decorate your existing tags.
Defining a skin
Here are some tips for defining a skin:
- Determine skin colors; use global constants so other skins can extend and override them.
- Create reusable templates for each taglib.
- Create templates with elements that can be overridden by an extending skin, so the whole template doesn't have to be rewritten to change the UI's appearance.
- Create a basic skin for your Web application and use it as the type for your Xkins instance.
- Avoid placing HTML inside Java code. If you have a taglib, servlet, or even a JSP page that has HTML code, consider migrating this HTML to an Xkins template.
We now walk through the phases of defining, designing, developing, and deploying Xkins in a simple Web application that requires skin management. In our example, we implement an application that registers subscribers for two online bookstores: Amazing and Barnie & Nibble. The application will be used in both sites (through a frame, a portlet, or whatever format the stores choose), but must have a look and feel specific to each bookstore.
To implement our application, we follow these steps:
- Obtain HTML pages with each skin
- Determine skins' templates
- Create the skins
- Use the skins
- Deploy the Web application
Obtain HTML pages with each skin
First of all, we receive the graphical design of the page provided by each bookstore. That material could be the page prototypes and should contain all possible page elements appearing in the application to be skinned (in our example, just one page)—see Figures 2 and 3.
As we can see, both pages have different colors, images, and field layouts. In addition, the required information indicators differ, plus Amazing's buttons are in GIF format, while Barnie & Nibble's button is an HTML button with styles.
Determine skins templates
Now we must clip pieces of these pages to generalize some templates for our application to use. We could start from zero, or we could base our HTML dissection in a basic skin used to create forms. This basic skin comes with the Xkins framework in Xkins Forms tags. Xkins Forms is an implementation of taglibs that uses Xkins to generate forms for Web applications.
The basic skin defines frame, field, button, etc. We should use this skin and add the templates our application needs (for instance, the branding). This basic skin also allows us to use Xkins Forms tags to generate our JSP pages.
Let's see a list of the templates we need:
frame: The table containing the whole form
frameMandatoryCaption: The text indicating mandatory fields
field: Coordinates the layout of both label and input
fieldLabel: The piece of text containing a label
fieldLabelMandatory: Piece of text indicating a mandatory label
fieldInput: Controls input
fieldInputMandatory: Indicates the input is mandatory
button: The command button to execute the action
branding: The branding corresponding to each bookstore
Create the skins
Once the different pieces of our UI are determined, we create both skins using Xkins. We start by naming them in the
<?xml version="1.0" encoding="UTF-8"?> <xkins> <skin name="base" url="/skins/forms/base" definition="/definition.xml"/> <skin name="amazing" url="/skins/forms/amazing" definition="/definition.xml"/> <skin name="bn" url="/skins/forms/bn" definition="/definition.xml"/> </xkins>
Now, we must create a directory structure in our Web application's
ROOT directory according to the defined configuration file shown in Figure 4.
In each subdirectory, we place the
definition.xml file describing the skin. We will walk through some templates of the skin. To see all of the example's templates, download the source code from Resources.
Let's look at the skin definition syntax contained in the
definition.xml file of Amazing's skin:
<skin name="amazing" extends="base"> </skin>
base is the default skin that comes with Xkins Forms and helps us skin our application. Amazing's skin extends it (so does Barnie & Nibble's). We now start overriding the base skin's templates for each skin, starting with the
<skin name="amazing" extends="base"> <template name="field" group="field"> <content><![CDATA[ $label $input ]]></content> </template> <template name="fieldLabel" group="field"> <content><![CDATA[ <td align=right style="font-family: verdana,arial,helvetica,sans-serif; font-size: x-small;"><b>$label:</b></td> ]]></content> </template> <template name="fieldLabelMandatory" group="field"> <content><![CDATA[ <td align=right style="font-family: verdana,arial,helvetica,sans-serif; font-size: x-small;"><b>$label:</b></td> ]]></content> </template> <template name="fieldInput" group="field"> <content><![CDATA[ <td colspan="$colspan" style="font-family: verdana,arial,helvetica,sans-serif; font-size: x-small;">$input (Optional)</td> ]]></content> </template> <template name="fieldInputMandatory" group="field"> <content><![CDATA[ <td colspan="$colspan"><strong>$input</strong></td> ]]></content> </template> </skin>
All the above templates are Velocity templates. Notice that the parameters are passed the template, and variables like
$colspan can be used. These parameters are passed by the
XkinsProcessor, which is called by the taglib.
Next, we start cutting and pasting the HTML from the mock pages to the Xkins templates. After that, we continue doing the same with the frame, the buttons, and the branding. The following code shows a piece of Barnie & Nibble's skin (just the
<skin name="bn" extends="base"> <path name="images" url="/images" /> <element name="spacer" path="images" url="/cleardot.gif"/> <!-- field templates --> <template name="field" group="field"> <content><![CDATA[ <td width="100%"> <table border=0 cellpadding=0 cellspacing=0 width="100%"> <tr> $label </tr> <tr> $input </tr> </table> ]]></content> </template> <template name="fieldLabel" group="field"> <content><![CDATA[ <td WIDTH="25%"><font size="-1" face="arial, helvetica, sans-serif">$label</font></td> ]]></content> </template> <template name="fieldLabelMandatory" group="field"> <content><![CDATA[ <td WIDTH="25%"><font size="-1" face="arial, helvetica, sans-serif">$label</font></td> ]]></content> </template> <template name="fieldInput" group="field"> <content><![CDATA[ <td colspan="$colspan" style="font-family: verdana,arial,helvetica,sans-serif; font-size: x-small;"> $input <br><b>(Optional) </b></td> ]]></content> </template> <template name="fieldInputMandatory" group="field"> <content><![CDATA[ <td WIDTH="25%">$input <img src="$res_mandatory" border="0"/></td> ]]></content> <element name="mandatory" path="images" url="/mandatory.gif"/> </template> <template name="nestedField" group="field"> <!-- jsp:bodyContent --> <content><![CDATA[ <td colspan="$res_fieldColspan" style="font-family: verdana,arial,helvetica,sans-serif; font-size: x-small;"> $bodyContent </td> ]]></content> </template> <!-- end field templates --> <!-- The rest of the templates go here --> </skin>
Use the skins
Now that we have a base skin and both required skins, we can create the JSP pages that will use these skins. To do that, we use the Xkins Forms tags because they use the templates defined in the base skin. You could create your own taglibs to use Xkins just like Xkins Forms does; then you wouldn't need to use Xkins Forms. But, Xkins Forms works fine with the Struts framework, which we use in this application.
We need two pages:
index.jsp: Where data entry is performed
done.jsp: Where the results print
Our example is just a demo application, so we won't really process the subscription request, just redirect it from
done.jsp. In a real application, a process would be completed between these pages.
Notice how Xkins is integrated with Struts in our example application. Xkins's taglibs do not replace Struts taglibs, they just decorate the page. You do not use, for example, the
<table> HTML tag, but instead
<forms:frame key="frame.title" width="30%"> to contain the form and add skinning capabilities. If your application uses Struts and you want to use Xkins, just place the Xkins Forms taglibs in your pages to decorate them. Then, pass all HTML tags used in your JSP pages to Xkins's templates and let Xkins generate the look and feel.
Though not shown in this example, Tiles can be used with Xkins. Xkins and Tiles can work together without overlapping: let Tiles manage page layout and Xkins render your components.
The following code shows the JSP page that renders the example's form, using Xkins Forms and Struts taglibs:
<html:form action="/subscribe" focus="lastName"> <xkin:template name="branding"/> <forms:frame key="frame.title" width="30%"> <forms:row> <forms:field key="subscription.name" mandatory="true" > <html:text property="name"/> </forms:field> </forms:row> <forms:row> <forms:field key="subscription.email" mandatory="true" > <html:text property="email"/> </forms:field> </forms:row> <forms:row> <forms:field key="subscription.document" mandatory="true" > <html:text property="document"/> </forms:field> </forms:row> <forms:row> <forms:field key="subscription.birthday"> <html:select property="month"> <option value="month">Month</option> </html:select> <html:select property="day"> <option value="day">Day</option> </html:select> </forms:field> </forms:row> <forms:buttons> <forms:button key="button.continue" default="true" onclick="document.subscribeForm.submit();"/> </forms:buttons> </forms:frame> </html:form>
In our example, we use Struts—which also resolves internationalization—as the UI framework. We use the
<xkins:template/> tag to render the branding template at the top of the page and the
<forms:*/> tags to render the form. We have a
frame that contains multiples rows. Each row has one or more
fields. When the frame renders, it asks each row to render itself. And each row asks the field to render. In each case, all these tags use the templates defined in the skin, so just changing the Xkins definition file changes the page's look and feel with no JSP modification.
buttons tag contains the page buttons, which are passed by parameter to the
frame template so you can place the form buttons where the template indicates. Here, we use the
button tag to render a button according to the skin. In Amazing's skin, we create a button with images (in a table); in Barnie & Nibble's skin, we use just HTML buttons. Notice also that the JSP page contains no HTML tags or CSS classes: the HTML rendering and styles are delegated to Xkins.
Deploy the Web application
Now that all the pieces are set, you just deploy the war file in a servlet container and start using the demo application. In the example, we integrate Xkins with Struts. You configure the
XkinsPlugin in the
struts-config.xml file like this:
<struts-config> <!-- Struts specific configuration goes here --> <plug-in className="ar.com.koalas.xkins.struts.XkinsPlugin"> <set-property property="config" value="/xkins-forms-definition.xml"/> <set-property property="autoReload" value="2000"/> <set-property property="skinType" value="base"/> </plug-in> </struts-config>
Business is expanding!
After successfully implementing our application, a new client, the Box Store, requests our services. The company gives us its application's look and feel, and we develop the skin, as shown in Figure 5.
This skin differs from the other bookstore skins, and shows how flexible the Xkins framework can be when developing skin-aware user interfaces. Check out this skin in the source code.
Other Xkins usages
Xkins can not only be used to skin a Web application. The Xkins architecture also allows you to:
- Create images on the fly, where, in each skin, you can create GIFs or Vector Markup Language, if the browser supports it, thereby determining the skin according to browser capabilities.
- Create different output according to client needs. You can have a skin to create HTML, another to create WML (Wireless Markup Language), and another for XML, determining skin according to the client's device type.
- Create reports, where, according to the user's preferences, one skin can create a PDF, another CSV, and another HTML.
- Create different HTML according to the browser type. You could create a skin for IE, another for Netscape, and another for Linux.
In summary, you could use Xkins as a single point for all templates in your application.
Even if you don't need Xkins's capabilities to create pieces of HTML from templates and just want to use CSS and images, you can use Xkins simply for the sake of organizing your files. For example, you can declare image paths, CSS file names, etc., with this Xkins definition:
<xkins> <skin name="organizer" url="/images"> <processor type="ar.com.koalas.xkins.processor.VelocityTemplateProcessor"/> <path name="images-bg" url="/backgrounds"/> <path name="images-icons" url="/icons"/> <path name="css" url="/css"/> <element name="logoffIcon" path="images-icons" url="/logoff.gif"/> <template name="stylesheet"> <content><![CDATA[ <link href="$res_stylesheet" type=text/css rel=stylesheet/> ]]></content> <element name="stylesheet" path="css" url="/formats.css"/> </template> </skin> </xkins>
In the JSP page, you can use these definitions like this:
<%@ taglib uri="/WEB-INF/tld/xkins.tld" prefix="xkins" %> <xkins:template name="stylesheet"/> <table background="<xkins:path name='images-bg'/>/myBg.gif"> <tr> <td> <img src="<xkins:resource name='logoffIcon'/>"/> </td> </tr> </table>
As you can see, you can organize your files with Xkins, and, if you change a path or an image name, just changing the Xkins definition is enough.
The future of Xkins: Xkins Faces
As we've seen, Xkins is based on externalizing component renderings from taglibs. This concept matches perfectly with the JavaServer Faces specification. JSF uses renderers to generate HTML (or whatever ML) for its components. Xkins can also be used to create renderers and to add skinning capabilities.
Xkins Faces is an implementation of JSF renderers that use Xkins. The main renderers implement the Decorator pattern for renderers of a JSF implementation. Xkins Faces define a skin type, so third-party vendors could create many skins that feature this same skin type. Thus, if you develop an application with JSF and Xkins Faces, you could download a new skin for your Web application, deploy it, and use it with no changes, since all templates needed by Xkins Faces are defined in the skin type. So in the near future, many skins could be available for download and use in your Web applications, or your users could compose their own skins based on existing ones and use templates directly from the Internet without deploying them.
Learn more about this topic
- Download the source code that accompanies this article
- Xkins's homepage
- Download Xkins
- The Struts homepage
- Velocity homepage
- JBYTE homepage
- Learn more about Groovy in "Build scripts with Groovy and Ant," Filippo Diotalevi (JavaWorld, October 2004)
- For more articles on UI design, browse the User Interface Design section of JavaWorld's Topical Index
- For more articles on development tools, browse the Development Tools section of JavaWorld's Topical Index