Skip to main content

Java Sketchbook: Digging into Java Web Start

September 1, 2005


The Java Web Start Sandbox
Using the Service APIs
in an Unsigned Application
Use Signed Code for Trusted Access
Use Pack 200 for Faster Downloads
   Compress Your .Jars
   Set Up Your Web Server
to Support Pack 200
Extra Polish
   Related Content
   File Associations

Java Web Start is a technology for deploying and updating
desktop Java applications easily from a web server. It's much more
than a launching protocol, however. JWS provides a sandbox for
safely running applications, as well as new compression methods to
speed up downloads, and some useful extensions to make your
application feel at home on the user's desktop. The "/pub/a/today/2005/08/11/webstart.html">first part of this
series covered just the basics. This second half will show you
everything you need to do to make your application run safely and
feel professional.

The Java Web Start Sandbox

In the age of viruses, spyware, and Internet Explorer bugs,
security has become one of the most important issues we face as
software developers. Java has always had very good support for
writing secure applications, being designed with security in mind
from the beginning. The lack of memory access and the security
manager ensure that an entire class of security bugs have been
wiped out. Still, there is always the possibility that a malicious
programmer will write a program to purposely delete files or steal
personal information. To tackle this issue, Java Web Start provides
something known as the sandbox.

The sandbox is a restricted environment that Java Web Start
applications run inside by default. Using the security manager and
some custom classloaders, it prevents the program from accessing
any inappropriate resources or damaging the host computer in any
way. In this way, the user is protected from malicious or badly
written programs. If you have ever programmed applets before, you
may think that this is familiar. Well, you are right. The JWS
sandbox was specifically designed with the applet security model in
mind. In fact, with the right JNLP, you can turn any existing applet
into a JWS app without any modifications. The sandbox also provides
some extensions to let you do more than you can with plain applets,
like access local files, use the clipboard, and print.

In practical terms the sandbox restricts your program from
talking to the host environment in any way. Files, network access,
and most system functions are off-limits, as are various ways of
getting around these restrictions, like using custom classloaders,
Runtime.exec() calls, or native libraries.

Well, this all sounds very limiting. How are you supposed to
write an application that does anything useful if you can't
work with files, print, or talk to the network? JWS provides three

  1. You can work within the restrictions. Just like an
    applet, the JWS sandbox will let you open a network connection back
    to the server from which the application was downloaded. You can load and
    save all data on the server, leaving nothing on the client machine
    other than the application itself. This works out surprisingly well
    for a number of application types. It also reduces maintenance and
    support headaches--if there's no data on the client, it can't
    become corrupted. If your application can work with the sandbox
    restrictions, I highly recommend you go this route.
  2. You can use the JWS Service APIs. The sandbox does allow
    your program to have limited access to the unsafe resources as long
    as it's under the control of the users. Using the Service APIs in
    the javax.jnlp package, you can ask the user to
    approve possibly unsafe actions like the opening of a file. The sandbox
    will open a file dialog window asking the user to select a file.
    Then the file will be passed to your application through a safe
    API. This way, the user has control over which file to use, if any,
    and your application can read files without using the unsafe API.

    The JWS Service APIs give you access to files, the clipboard,
    printing, and a limited form of data persistence. Java 1.6
    (Mustang) will introduce a new SocketService to give your program
    conditional network access. With these services, you can do almost
    anything while staying inside the sandbox, provided the user grants

  3. You can go outside of the sandbox. If your program really,
    really needs unsafe access to the host computer (for example, your
    program wants to use the JDIC native libraries), then you can ship
    your program as a signed and trusted application. This means that
    you must digitally sign the components of your program and place
    them on your website with the same signature.

    The first time a user downloads your signed program, she will be
    presented with a dialog box asking her to accept the certificate
    and continue with the installation. If she says no, the
    installation will be aborted. If she say yes, then your program
    will be installed and launched outside of the sandbox, giving you
    access to the full Java APIs. This only works, of course, if your
    application is signed, because users need to be able to trust that
    the program really comes from you. Even when it is signed, you are asking
    your users to trust you, and some of them may be hesitant to install
    a program that could potentially do anything. I recommend going
    with one of the other two options if at all possible, since they
    involve less risk to the user's computer and don't display a
    potentially scary security dialog box.

Using the Service APIs in an Unsigned Application

If you want your program to work inside of the sandbox but still
access local resources, you need to use the Service APIs. There are
APIs to cover all of the major resources you could need to access
(files, clipboard, printing, etc.), but they all operate pretty much
the same way. When you request an unsafe feature using a Service
API, the sandbox will show the user a dialog box asking
permission. If the user agrees, you will be able to access that
feature. If the user declines, you will get a null value and you'll
be unable to use that feature. For example, suppose the My
Pirate program from the previous installment of this series let users
select a photo from their hard drives to use as their icon in the
game. To do this safely, you would need to use

import javax.jnlp.*;
// more imports ...
public class FileOpener extends ActionListener {
    public void actionPerformed(ActionEvent evt) {
        try {
            FileOpenService open = (FileOpenService)
            FileContents fc = open.openFileDialog(null,null);
            // do stuff with the file contents
            // like fc.getName();
        } catch (Exception ex) {
            System.out.println("exception: " + ex);

Above is an ActionListener that asks the user for
a pirate configuration file. First it obtains a
FileOpenService by looking it up from the
ServiceManager and then casting to the right type.
Then it calls the openFileDialog() method with
null for the directory and file type arguments. When
it makes the open call, the sandbox will show a dialog box asking
the user for the file. Once the user selects a file, the action
listener will receive a FileContents object, which has
safe methods for retrieving file information.

Note: Mac OS X supports the javax.jnlp.* APIs
but it does not provide a standalone .jar to compile against as part
of its JDK. You will need to download Sun's JDK for another
platform and compile against jnlp.jar from that.
Being 100 percent Java code, it will work fine on the Mac.

Use Signed Code for Trusted Access

If your program needs full access to the user's computer, then
you must use a signed application. To make your program signed, you
must first digitally sign the .jars in your program, and then change
your JNLP to request full access. Digitally signed .jars require a
keystore, so that's where we'll start.

First you need a keystore. This is a file on your development
computer that stores the actual digital keys to prove that your .jars are
coming from you. It requires a password for access, ensuring that
only you are signing your .jars and not someone who has gained
access to your computer. If you already have professional keys from
a trusted certificate authority (like "">VeriSign or "">Thawte), then you can use those. If you
are just doing development work, you can use a self-signed
certificate to try out the process. To generate your own keystore
with a self-signed certificate, use the keytool program
like this:

[prettify]keytool -genkey -keystore mykeystore -alias mykey

The command above will generate a new key called
mykey and place it in a keystore file called
mykeystore. When you execute the above command, the keytool
application will ask you a series of questions to identify
you and your organization, and will also have you create a password to
protect the keystore. Once your keystore is ready, you can sign your
.jars like this:

[prettify]jarsigner -keystore mykeystore temp.jar mykey

The jarsigner command above will open the keystore called
mykeystore, retrieve the key named mykey,
and use the key to sign the temp.jar file. Once your
.jars are signed, you can upload them to your web server.

The last step is to change the JNLP file to request full
security permissions, like this:

[prettify]<?xml version="1.0" encoding="UTF-8"?>
<jnlp codebase=""
    ....  the rest of the JNLP file

With the new JNLP and the signed .jars, Java Web Start will ask
your users to approve the certificate (including the identifying
information you put into the keystore), and then run your
application will full permissions. If you will be signing your .jar
files over and over, you may want to use the "">signjar Ant
to automate the process. If you are storing your code and
build files in revision control (like a project, for
example) be sure to put the actual password in a private property
that no one else can see.

For more information on signed .jars, please read the "">
man page for keytool
and this "">great article, which
shows more examples of using keytool and how to get a
free certificate that works with most browsers' built-in
authorities (meaning you won't get the invalid-certificate

Use Pack 200 for Faster Downloads

Once you have your program signed and running, you may want to
think about improving its download time. Software has a tendency to
grow larger over time, producing ever-larger files to download. To
speed up the downloads, Java 1.5 introduced a new form of
compression specifically designed for Java code: Pack 200.

Pack 200 was named for the JSR that defined it, and it can
compress some .jars by a factor of five, in addition to the typical
2x gzip compression that the Pack 200 tool also applies. This can
result in compressed .jars being ten or 15 percent of their uncompressed size. For a
widely deployed application, this will greatly speed up
installation time for your users and greatly reduce your bandwidth
bill. Pack 200 only works well on Java bytecode, so if your .jar has
a lot of things in it that aren't class files (like images) you
won't see as much compression. Pack 200 takes advantage of
duplicated information between classes, so larger .jars will compress

Compress Your .Jars

Compressing your .jars with Pack 200 is very easy. Java 1.5 comes
with the pack200 command-line program, which converts a
standard .jar into a packed .jar. For example, to compress
mypirate.jar, you would do this from the command

[prettify]pack200 mypirate.jar.pack.gz mypirate.jar

This will compress mypirate.jar into a new
mypirate.jar.pack.gz file without removing the
original .jar file. Once packed, you should upload both files to the
web server where you will host your application. More on why you
should upload both in a second.

If you are using signed .jars, the process is slightly more
complicated. pack200 will rearrange your classfiles
and the signature depends on the bytes being in a particular order.
To sign a packed .jar, you will need to pack, unpack, and then
sign the .jar before repacking it. This will preserve the order of
the bytes for the signature to work. You can read more on this
process in the "">
Pack 200 section
of the Java 1.5 deployment guide.

If you are building and deploying your application frequently,
you can use an Ant task from the open source "">java-pack200-ant-task
on to integrate Pack 200 into your build

Set Up Your Web Server to Support Pack 200

The server side of Pack 200 is more complicated than the
compression phase. Older Java Web Start clients may not understand
Pack 200, or it may be turned off for some reason. To support all
kinds of JWS clients, Pack 200 has a special negotiation protocol
using HTTP headers to download the right .jar. This means you need
to set up your web server to support these special headers.
Depending on your web server, there are many ways you could do this,
but the easiest way is to use a special servlet that comes with the
1.5 JDK, called JnlpDownloadServlet. The following
instructions are for Tomcat, but you can adapt them for any J2EE
application server.

In the sample/jnlp/servlet/ directory of the Sun J2SE 1.5
JDK, you will find a README file and a couple of .jars. The
JnlpDownloadServlet is in the jnlp-servlet.jar
file. You need to copy this .jar to the WEB-INF/lib directory
of whatever servlet context you will use to serve up your
application .jar files. Then you must modify the web.xml file
to include the following lines:


The first section, servlet, defines the
JnlpDownloadServlet itself. The second section maps
all .jar files to this servlet. This means when the client
requests a .jar file, the request will go through the download
servlet. For any other files (say, image or .jnlp files), the
request will proceed as normal. The servlet will look at the HTTP
headers to detect if the client supports Pack 200. If the client
does support Pack 200 and there are packed .jar files available,
then the servlet will send out the packed versions; otherwise it
will send the normal .jars. This is why you must have both the
standard .jars and the packed ones on your server.

You don't need to modify your JNLP file to use Pack 200. Still
refer to your .jars by the normal name (for example,
mypirate.jar). The JWS client and the download servlet
will transparently use Pack 200 when possible, and of course, older
clients will still use the normal .jars.

To further improve download speed, the
JnlpDownloadServlet also supports .jar diffs for
application updates. This means it sends only the difference
between the old and new .jars instead of the entire new .jar. .Jar
diffs are beyond the scope of this article, but you can read more
about them in the "">
Download Servlet Guide

Extra Polish

Java Web Start provides a lot of odds and ends to help you
polish your application. These extras can add a sense of
professionalism. It's worth taking the time to evaluate your
application and use them where appropriate.


Java Web Start allows the user to create an icon for your
program if the user requests it. However, you can add hints to
prompt the user and request an icon on the desktop. If the user's
operating system has the concept of a Start menu, you can also
specify a particular place in the menu to put your program and any
extra resources. You can do this using the

element and submenu
attribute in your JNLP file. For example, if I would like the My
program to be installed on the desktop and in the
Aye-Soft/My Pirate! menu, then I would need to change
my JNLP file to look like this:

[prettify]<jnlp codebase=""
        <title>My Pirate!</title>
            <!-- put a shortcut on the desktop -->
            <!-- put shortcut in start menu too -->
            <menu submenu="Aye-Soft">
    ... the rest of the JNLP

The element inside of
asks Java Web Start to put an icon on
the desktop if the user allows it. The

element will put a shortcut with the
icon and title of the application (as specified by the
</code> element above) in the<br /> Aye-Soft menu.</p> <p><a name="related_content"></a></p> <h5 id="related_content">Related Content</h5> <p>The newest version of Java Web Start supports links for related<br /> content. Using the <code class="prettyprint">related-content</code> element, you can<br /> add a link to extra documentation or other resources that should be<br /> installed in the start menu alongside your application.</p> <pre class="prettyprint"><code>[prettify]<jnlp codebase=""     href="mypirate.jnlp">     <information>         <title>My Pirate!</title>         <shortcut>             <menu submenu="My Pirate"/>             <desktop/>         </shortcut>         <related-content href="readme.html">             <title>Instructions</title>         </related-content> ... the rest of the JNLP </code></pre> [/prettify]</p> <p>The <code class="prettyprint"><related-content></code> element in the JNLP<br /> file above will put a link to the <i>readme.html</i> file in the<br /> menu with the title Instructions.</p> <p><a name="file_associations"></a></p> <h5 id="file_associations">File Associations</h5> <p>You can request that your application be registered to support<br /> certain file types. This means when the user launches a file of the<br /> type you set, your program will be launched. You must specify the<br /> association using both an extension (the characters after the<br /> <code class="prettyprint">.</code> in a filename) and a MIME type.</p> <pre class="prettyprint"><code>[prettify]<jnlp codebase=""     href="mypirate.jnlp">     <information>     <title>My Pirate!</title>     <association extensions="pirate"         mime-type="x-application/pirate"/> ... the rest of the JNLP </code></pre> [/prettify]</p> <p>The association element in the JNLP above would register the<br /> application as a handler for all <code class="prettyprint">.pirate</code> files and all<br /> files with a MIME type of <code class="prettyprint">x-application/pirate</code>. You can<br /> set up more than one association by using multiple elements.</p> <p><a name="conclusion"></a></p> <h2 id="conclusion">Conclusion</h2> <p>Java Web Start is a powerful technology for delivering<br /> applications as easily as web apps, but with the rich interface and<br /> features of standard desktop apps. It is secure, easy to use, and<br /> gives your application fast updating capabilities. For more<br /> information on Java Web Start, take a look at Sun's complete<br /> <a href=""><br /> Java Web Start Developers Guide</a>. For any new application, or<br /> even old ones that you would like to deploy widely, I recommend you<br /> try out Java Web Start and see how it can improve your maintenance<br /> and your users' experience.</p> <p><a name="resources"></a></p> <h2 id="resources">Resources</h2> <ul> <li><a href="/sites/all/modules/pubdlcnt/pubdlcnt.php?file=/today/2005/09/01/">Sample<br /> code</a> for this article</li> <li><a href=<br /> ""><br /> Java Web Start Developers Guide</a></li> <li><a href="">The Unofficial Java Web<br /> Start/JNLP FAQ</a></li> </ul> <p><csinclude record="au/{cs.r.id_author}" template="short_bio.view" /></p> <p><csinclude record="ct/{id_coltype}" template="ct/article_link.view"></p> <div class="pad3x0"> <table border="0" cellpadding="0" cellspacing="0" width="100%" bgcolor="#000000"> <tr> <td><img src=""<br /> width="1" height="1" border="0" alt=" " /></td> </tr> </table> </div> <div id="author-info"> <a href="/author/josh-marinacci">Josh Marinacci</a> first tried Java in 1995 at the request of his favorite TA and has never looked back. </div> <!-- --> <!-- <div class="links">» </div> --> <div class="taxonomy">Related Topics >> <a href="/articles/topic/14">GUI</a>   |    </div> <div class="node-links"><span class="label" style="font-weight: bold; margin-right: 0.5em; float: left;">Article Links >></span> <ul class="links"><li class="comment_forbidden first"><span><a href="/user/login?destination=comment%2Freply%2F219608%23comment-form">Login</a> or <a href="/user/register?destination=comment%2Freply%2F219608%23comment-form">register</a> to post comments</span></li> <li class="print_html"><a href="/print/219608" title="Display a printer-friendly version of this page." class="print-page" rel="nofollow">Printer-friendly version</a></li> <li class="sharethis_link"><a href="" class="sharethis-link" title="Java Sketchbook: Digging into Java Web Start" rel="nofollow">ShareThis</a></li> <li class="statistics_counter last"><span>26007 reads</span></li> </ul> </div> </div> <!-- /article --> </div> </div> <!-- /main-content --> </div></div> <!-- /content-column --> <div id="sidebar-first" class="sidebar"> <div id="block-menu-menu-get-involved" class="block"> <div class="block-inner"> <h2>Get Involved</h2> <div class="content"><ul class="menu"> <li class="leaf first"><a href="/about-javanet" title="About">About</a></li> <li class="leaf"><a href="" title="Adopt">Adopt a JSR</a></li> <li class="leaf"><a href="/create-project" title="Create a Project">Create a Project</a></li> <li class="leaf last"><a href="/javanet-linking-instructions" title="Link an offiste project">Link an Offsite Project</a></li> </ul> </div> </div> </div> <!-- /block --> <div id="block-menu-menu-get-informed" class="block"> <div class="block-inner"> <h2>Get Informed</h2> <div class="content"><ul class="menu"> <li class="leaf first"><a href="/articles" title="">Articles</a></li> <li class="leaf"><a href="/blogfront" title="">Blogs</a></li> <li class="leaf"><a href="/events" title="">Events</a></li> <li class="leaf"><a href="" title="Subscribe to Java Magazine">Java Magazine</a></li> <li class="leaf last"><a href="" title="Java Training and Certification at Oracle University">Oracle University</a></li> </ul> </div> </div> </div> <!-- /block --> </div> <!-- /sidebar-first --> </div></div> <!-- /columns --> <div id="tertiary-content"><div id="block-menu-menu-footer-menu" class="block"> <div class="block-inner"> <div class="content"><ul class="menu"> <li class="leaf first"><a href="/contact" title="">Feedback</a></li> <li class="leaf"><a href="/javanet-faq" title="">FAQ</a></li> <li class="leaf"><a href="/javanet-web-site-terms-use" title="">Terms of Use</a></li> <li class="leaf"><a href="" title="">Privacy</a></li> <li class="leaf last"><a href="" title="">Trademarks</a></li> </ul> </div> </div> </div> <!-- /block --> </div> <!-- /tertiary-content --> <div id="footer"> <div id="footer-region"><div id="block-block-27" class="block"> <div class="block-inner"> <div class="content"><div style="width: 59%; float: left;"> <p>Your use of this web site or any of its content or software indicates your agreement to be bound by these <a href="/javanet-web-site-terms-use">Terms of Participation</a>.</p> <p>Copyright © 2015, Oracle and/or its affiliates. All rights reserved. Oracle and Java are registered trademarks of Oracle and/or its affiliates. Other names may be trademarks of their respective owners.</p> </div> <div style="width: 39%; float: left; text-align: right;"> <div style="float: right;"><a href=""><img src="/sites/all/themes/java_adaptive/images/oracle.png"></a><a href=""><img src="/sites/all/themes/java_adaptive/images/kenai.png"></a><a href=""><img src="/sites/all/themes/java_adaptive/images/cognisync.png"></a><br /> <div class="powered-by">Powered by Oracle, Project Kenai and Cognisync</div></div> </div></div> </div> </div> <!-- /block --> </div> <!-- /footer-region --> </div> <!-- /footer --> </div> <!-- /container --> <script type="text/javascript" src="/sites/all/modules/jquery_update/replace/jquery.min.js?e"></script> <script type="text/javascript" src="/misc/drupal.js?e"></script> <script type="text/javascript" src="/sites/all/modules/datetweaks/js/datetweaks.js?e"></script> <script type="text/javascript" src="/sites/all/modules/datetweaks/js/date-functions/date-functions.js?e"></script> <script type="text/javascript" src="/sites/all/modules/datetweaks/js/timepicker/jquery.timepicker.js?e"></script> <script type="text/javascript" src="/sites/all/modules/fivestar/js/fivestar.js?e"></script> <script type="text/javascript" src="/sites/all/modules/lightbox2/js/lightbox.js?e"></script> <script type="text/javascript" src="/sites/all/modules/panels/js/panels.js?e"></script> <script type="text/javascript" src="/sites/all/libraries/prettify/prettify.js?e"></script> <script type="text/javascript" src="/sites/all/modules/prettify/prettify.loader.js?e"></script> <script type="text/javascript" src="/sites/all/modules/extlink/extlink.js?e"></script> <script type="text/javascript" src="/sites/all/modules/custom_search/js/custom_search.js?e"></script> <script type="text/javascript" src="/sites/all/modules/sharethis/sharethis/jquery.sharethis.js?e"></script> <script type="text/javascript" src="/sites/all/modules/sharethis/sharethis.js?e"></script> <script type="text/javascript" src="/sites/all/modules/ajax/jquery/jquery.a_form.packed.js?e"></script> <script type="text/javascript" src="/sites/all/modules/ajax/ajax.js?e"></script> <script type="text/javascript" src="/sites/all/themes/adaptivetheme/adaptivetheme/js/at-scripts.js?e"></script> <script type="text/javascript" src="/sites/all/themes/java_adaptive/js/java_custom.js?e"></script> <script type="text/javascript" src="/sites/all/themes/java_adaptive/js/forums.js?e"></script> <script type="text/javascript" src="/sites/all/themes/java_adaptive/js/java_search.js?e"></script> <script type="text/javascript" src="/sites/all/modules/ajax_views_refresh/ajax_views_refresh.js?e"></script> <script type="text/javascript"> <!--//--><![CDATA[//><!-- jQuery.extend(Drupal.settings, { "basePath": "/", "datetweaks": { "use_dropdown": true }, "fivestar": { "titleUser": "Your rating: ", "titleAverage": "Average: ", "feedbackSavingVote": "Saving your vote...", "feedbackVoteSaved": "Your vote has been saved.", "feedbackDeletingVote": "Deleting your vote...", "feedbackVoteDeleted": "Your vote has been deleted." }, "lightbox2": { "rtl": 0, "file_path": "/(\\w\\w/)sites/default/files", "default_image": "/sites/all/modules/lightbox2/images/brokenimage.jpg", "border_size": 0, "font_color": "000", "box_color": "fff", "top_position": "", "overlay_opacity": "0.8", "overlay_color": "000", "disable_close_click": 1, "resize_sequence": 0, "resize_speed": 400, "fade_in_speed": 400, "slide_down_speed": 600, "use_alt_layout": 1, "disable_resize": 0, "disable_zoom": 0, "force_show_nav": 0, "show_caption": 1, "loop_items": 0, "node_link_text": "View Image Details", "node_link_target": 0, "image_count": "Image !current of !total", "video_count": "Video !current of !total", "page_count": "Page !current of !total", "lite_press_x_close": "press \x3ca href=\"#\" onclick=\"hideLightbox(); return FALSE;\"\x3e\x3ckbd\x3ex\x3c/kbd\x3e\x3c/a\x3e to close", "download_link_text": "", "enable_login": false, "enable_contact": false, "keys_close": "c x 27", "keys_previous": "p 37", "keys_next": "n 39", "keys_zoom": "z", "keys_play_pause": "32", "display_image_size": "original", "image_node_sizes": "()", "trigger_lightbox_classes": "", "trigger_lightbox_group_classes": "", "trigger_slideshow_classes": "", "trigger_lightframe_classes": "", "trigger_lightframe_group_classes": "", "custom_class_handler": 0, "custom_trigger_classes": "", "disable_for_gallery_lists": true, "disable_for_acidfree_gallery_lists": true, "enable_acidfree_videos": true, "slideshow_interval": 5000, "slideshow_automatic_start": true, "slideshow_automatic_exit": true, "show_play_pause": true, "pause_on_next_click": false, "pause_on_previous_click": true, "loop_slides": false, "iframe_width": 900, "iframe_height": 700, "iframe_border": 1, "enable_video": 0 }, "prettify": { "linenums": false, "match": ".content", "nocode": "no-code", "custom": [ ], "markup": { "code": true, "pre": true, "precode": true } }, "extlink": { "extTarget": "_blank", "extClass": 0, "extSubdomains": 1, "extExclude": "", "extInclude": "", "extCssExclude": "", "extCssExplicit": "", "extAlert": 0, "extAlertText": "This link will take you to an external web site. We are not responsible for their content.", "mailtoClass": 0 }, "custom_search": { "form_target": "_self", "solr": 0 } }); //--><!]]> </script> <!-- Begin SiteCatalyst code --> <script language="JavaScript" src="/images/s_code_remote.js"></script> <script language="JavaScript"> <!-- var s_channel="article"; //--></script> <!-- End SiteCatalyst code --> <script type="text/javascript" src="/sites/all/modules/browserclass/browserclass.js?e"></script> <!-- --> </body> </html>