blob: f2198d856bb470aeb84f612d5b271f9bb4578719 [file] [log] [blame]
Richard S. Hall677f5892007-07-10 15:29:11 +00001<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
Karl Pauls0b9d26f2009-09-06 21:15:38 +00002<html><head>
Richard S. Hall677f5892007-07-10 15:29:11 +00003
4
5
Karl Pauls0b9d26f2009-09-06 21:15:38 +00006 <title>Apache Felix - Apache Felix Shell Service</title>
Richard S. Hall677f5892007-07-10 15:29:11 +00007 <link rel="stylesheet" href="apache-felix-shell-service_files/site.css" type="text/css" media="all">
Karl Pauls0b9d26f2009-09-06 21:15:38 +00008 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
9 </head><body>
10 <div class="title"><div class="logo"><a href="http://felix.apache.org/site/index.html"><img alt="Apache Felix" src="apache-felix-shell-service_files/logo.png" border="0"></a></div><div class="header"><a href="http://www.apache.org/"><img alt="Apache" src="apache-felix-shell-service-Dateien/apache.png" border="0"></a></div></div>
Richard S. Hall677f5892007-07-10 15:29:11 +000011 <div class="menu">
Karl Pauls63100252008-04-21 22:17:48 +000012<ul>
13 <li><a href="http://felix.apache.org/site/news.html" title="news">news</a></li>
14 <li><a href="http://felix.apache.org/site/license.html" title="license">license</a></li>
Karl Pauls0b9d26f2009-09-06 21:15:38 +000015 <li><a href="http://felix.apache.org/site/downloads.cgi" rel="nofollow">downloads</a></li>
Karl Pauls63100252008-04-21 22:17:48 +000016 <li><a href="http://felix.apache.org/site/documentation.html" title="documentation">documentation</a></li>
17 <li><a href="http://felix.apache.org/site/mailinglists.html" title="mailinglists">mailing lists</a></li>
18 <li><a href="http://felix.apache.org/site/contributing.html" title="Contributing">contributing</a></li>
Karl Pauls0b9d26f2009-09-06 21:15:38 +000019 <li><a href="http://www.apache.org/" rel="nofollow">asf</a></li>
20 <li><a href="http://www.apache.org/foundation/sponsorship.html" rel="nofollow">sponsorship</a></li>
21 <li><a href="http://www.apache.org/foundation/thanks.html" rel="nofollow">sponsors</a>
Karl Pauls63100252008-04-21 22:17:48 +000022<!-- ApacheCon Ad -->
23<iframe src="apache-felix-shell-service_files/button.html" style="border-width: 0pt; float: left;" frameborder="0" height="135" scrolling="no" width="135"></iframe>
24<p style="height: 100px;">
25<!-- ApacheCon Ad -->
26</p></li></ul> </div>
Richard S. Hall677f5892007-07-10 15:29:11 +000027 <div class="main">
28<h1><a name="ApacheFelixShellService-ApacheFelixShellService"></a>Apache Felix Shell Service</h1>
29
30<ul>
Karl Pauls0b9d26f2009-09-06 21:15:38 +000031 <li><a href="#ApacheFelixShellService-overview">Overview</a></li>
32 <li><a href="#ApacheFelixShellService-service">How the Shell Service Works</a></li>
33 <li><a href="#ApacheFelixShellService-commands">How Commands Work</a></li>
34 <li><a href="#ApacheFelixShellService-creating">Creating a Command</a></li>
35 <li><a href="#ApacheFelixShellService-security">Security and the Shell Service</a></li>
36 <li><a href="#ApacheFelixShellService-feedback">Feedback</a></li>
Richard S. Hall677f5892007-07-10 15:29:11 +000037</ul>
38
39
40<p><a name="ApacheFelixShellService-overview"></a></p>
41
42<h2><a name="ApacheFelixShellService-Overview"></a>Overview</h2>
43
44<p>In order to interact with Felix it is necessary to have some sort of
45interactive shell that allows you to issue commands to the framework
46and to obtain information from it. The OSGi specification does not
47define how an OSGi framework should provide this interactivity. Felix
48defines a shell service for creating and executing arbitrary commands.
49The shell service does not define a user interface, only a service API.</p>
50
51<p>The benefit of the Felix shell service approach is that it is possible to:</p>
52
53<ul>
54 <li>have multiple shell user interfaces (e.g., textual and graphical),</li>
55 <li>add custom commands to the shell (i.e., bundles can make commands available via the shell service), and</li>
56 <li>use the shell service from other bundles/services.</li>
57</ul>
58
59
60<p>The remainder of this document describes how the shell service works
61and how to create custom commands for it. This document does not
62describe how to use the command shell, nor does it describe the
63text-based or GUI-based user interfaces that are available for the
64shell.</p>
65
66<p><a name="ApacheFelixShellService-service"></a></p>
67
68<h2><a name="ApacheFelixShellService-HowtheShellServiceWorks"></a>How the Shell Service Works</h2>
69
70<p>The Felix shell service is intended to be a simple, but extensible
71shell service that can have multiple user interface implementations,
72all of which are independent from the Felix framework. The shell
73service is currently not intended to be sophisticated, rather it is
74just a mechanism to execute commands. The shell service maintains a
75list of command services, each of which have a unique command name. The
76shell service is defined by the following service interface:</p>
77
Karl Pauls0b9d26f2009-09-06 21:15:38 +000078<style type="text/css">
79@import url(/confluence/download/resources/confluence.ext.code:code/shStyles.css);
80</style>
81<!--[if IE]>
82<style type="text/css">
83 .code textarea, .code input { padding: 0 !important; }
84</style>
85<![endif]-->
86<script class="javascript" src="apache-felix-shell-service_files/shCore.js"></script>
87<script class="javascript" src="apache-felix-shell-service_files/shBrushCSharp.js"></script>
88<script class="javascript" src="apache-felix-shell-service_files/shBrushPhp.js"></script>
89<script class="javascript" src="apache-felix-shell-service_files/shBrushJScript.js"></script>
90<script class="javascript" src="apache-felix-shell-service_files/shBrushVb.js"></script>
91<script class="javascript" src="apache-felix-shell-service_files/shBrushSql.js"></script>
92<script class="javascript" src="apache-felix-shell-service_files/shBrushXml.js"></script>
93<script class="javascript" src="apache-felix-shell-service_files/shBrushShell.html"></script>
94<script class="javascript" src="apache-felix-shell-service_files/shBrushDelphi.js"></script>
95<script class="javascript" src="apache-felix-shell-service_files/shBrushPython.js"></script>
96<script class="javascript" src="apache-felix-shell-service_files/shBrushJava.js"></script>
97<div class="code">
98<textarea name="newcodemacro" class="java:nocontrols:nogutter" rows="10" readonly="readonly">package org.apache.felix.shell;
Richard S. Hall677f5892007-07-10 15:29:11 +000099
Karl Pauls0b9d26f2009-09-06 21:15:38 +0000100public interface ShellService
Richard S. Hall677f5892007-07-10 15:29:11 +0000101{
Karl Pauls0b9d26f2009-09-06 21:15:38 +0000102 public String[] getCommands();
103 public String getCommandUsage(String name);
104 public String getCommandDescription(String name);
105 public ServiceReference getCommandReference(String name);
106 public void executeCommand(
107 String commandLine, PrintStream out, PrintStream err)
108 throws Exception;
109}</textarea>
110<script class="javascript">
111 if(!window.newcodemacro_initialised)
112 {
113 window.newcodemacro_initialised = true;
114 window.oldonloadmethod = window.onload;
115 window.onload = function(){
116 dp.SyntaxHighlighter.HighlightAll('newcodemacro');
117 if(window.oldonloadmethod)
118 {
119 window.oldonloadmethod();
120 }
121 }
122 }
123
124</script>
125</div>
126
Richard S. Hall677f5892007-07-10 15:29:11 +0000127
128<p>Using the shell service interface, it is possible to access and
129execute available commands. The shell service methods perform the
130following functions:</p>
131
132<ul>
133 <li><tt>getCommands()</tt> - returns an array of strings that correspond to the names of the installed shell commands.</li>
134 <li><tt>getCommandUsage()</tt> - returns the command usage string for a particular command name</li>
135 <li><tt>getCommandDescription()</tt> - returns a short description for a particular command name.</li>
136 <li><tt>getCommandReference()</tt> - returns the service reference for a particular command name.</li>
137 <li><tt>executeCommand()</tt> - executes a particular command using the specified command line and print streams.</li>
138</ul>
139
140
141<p>Most of the shell service methods require no explanation except for
142the executeCommand() method. Even though this method is the most
143complex, it is still fairly simplistic. The assumption of the shell
144service is that a command line will be typed by the user (or perhaps
145constructed by a GUI) and passed into it for execution. The shell
146service interprets the command line in a very simplistic fashion; it
147takes the leading string of characters terminated by a space character
148(not including it) and assumes that this leading token is the command
149name. Consider the following command line:</p>
150
Karl Pauls0b9d26f2009-09-06 21:15:38 +0000151<div class="preformatted panel" style="border-width: 1px;"><div class="preformattedContent panelContent">
Karl Pauls63100252008-04-21 22:17:48 +0000152<pre>update 3 http://www.foo.com/bar.jar
Richard S. Hall677f5892007-07-10 15:29:11 +0000153</pre>
154</div></div>
155
156<p>The shell service interprets this as an <tt>update</tt> command and
157will search for a command service with the same name. If a
158corresponding command service is not found, then it will print an error
159message to the error print stream. If a corresponding command service
160is found, then it will pass the entire command line string and the
161print streams into the <tt>executeCommand()</tt> method of the command service (for a more detailed description of command services, see the next section).</p>
162
163<p>Notice that there is no method to add commands to the shell service
164interface. This is because commands are implemented as OSGi services
165and the shell service listens for service events and when a command
166service registers/unregisters it automatically updates its list of
167commands accordingly.</p>
168
169<p><a name="ApacheFelixShellService-commands"></a></p>
170
171<h2><a name="ApacheFelixShellService-HowCommandsWork"></a>How Commands Work</h2>
172
173<p>All commands available in the shell service are implemented as OSGi
174services. The advantage of this approach is two-fold: the shell service
175can leverage OSGi service events to maintain its list of available
176commands and the set available commands is dynamically extendable by
177installed bundles. The command service interface is defined as follows:</p>
178
Karl Pauls0b9d26f2009-09-06 21:15:38 +0000179<div class="code">
180<textarea name="newcodemacro" class="java:nocontrols:nogutter" rows="10" readonly="readonly">package org.apache.felix.shell;
Richard S. Hall677f5892007-07-10 15:29:11 +0000181
Karl Pauls0b9d26f2009-09-06 21:15:38 +0000182public interface Command
Richard S. Hall677f5892007-07-10 15:29:11 +0000183{
Karl Pauls0b9d26f2009-09-06 21:15:38 +0000184 public String getName();
185 public String getUsage();
186 public String getShortDescription();
187 public void execute(String line, PrintStream out, PrintStream err);
188}</textarea>
189<script class="javascript">
190 if(!window.newcodemacro_initialised)
191 {
192 window.newcodemacro_initialised = true;
193 window.oldonloadmethod = window.onload;
194 window.onload = function(){
195 dp.SyntaxHighlighter.HighlightAll('newcodemacro');
196 if(window.oldonloadmethod)
197 {
198 window.oldonloadmethod();
199 }
200 }
201 }
202
203</script>
204</div>
205
Richard S. Hall677f5892007-07-10 15:29:11 +0000206
207<p>The semantics of the command service methods are:</p>
208
209<ul>
210 <li><tt>getName()</tt> - returns the name of the command; this must not contain whitespace and must be unique.</li>
211 <li><tt>getUsage()</tt>
212- returns the usage string of the command; this should be one line and
213as short as possible (this is used for generating the help command
214output).</li>
215 <li><tt>getShortDescription()</tt> - returns a short
216description of the command; this should be one line and as short as
217possible (this is used for generating the help command output).</li>
218 <li><tt>execute()</tt> - executes the command's functionality using supplied command line and print streams.</li>
219</ul>
220
221
222<p><a name="ApacheFelixShellService-creating"></a></p>
223
224<h2><a name="ApacheFelixShellService-CreatingaCommand"></a>Creating a Command</h2>
225
226<p>The following example creates a simple version of the <tt>start</tt> command.</p>
227
Karl Pauls0b9d26f2009-09-06 21:15:38 +0000228<div class="code">
229<textarea name="newcodemacro" class="java:nocontrols:nogutter" rows="10" readonly="readonly">package test;
Richard S. Hall677f5892007-07-10 15:29:11 +0000230
Karl Pauls0b9d26f2009-09-06 21:15:38 +0000231import java.io.PrintStream;
232import java.net.URL;
233import java.net.MalformedURLException;
234import java.util.StringTokenizer;
Richard S. Hall677f5892007-07-10 15:29:11 +0000235
Karl Pauls0b9d26f2009-09-06 21:15:38 +0000236import org.osgi.framework.*;
237import org.apache.felix.shell.ShellService;
238import org.apache.felix.shell.Command;
Richard S. Hall677f5892007-07-10 15:29:11 +0000239
Karl Pauls0b9d26f2009-09-06 21:15:38 +0000240public class MyStartCommandImpl implements Command
Richard S. Hall677f5892007-07-10 15:29:11 +0000241{
Karl Pauls0b9d26f2009-09-06 21:15:38 +0000242 private BundleContext m_context = null;
Richard S. Hall677f5892007-07-10 15:29:11 +0000243
Karl Pauls0b9d26f2009-09-06 21:15:38 +0000244 public MyStartCommandImpl(BundleContext context)
Richard S. Hall677f5892007-07-10 15:29:11 +0000245 {
246 m_context = context;
247 }
248
Karl Pauls0b9d26f2009-09-06 21:15:38 +0000249 public String getName()
Richard S. Hall677f5892007-07-10 15:29:11 +0000250 {
Karl Pauls0b9d26f2009-09-06 21:15:38 +0000251 return "mystart";
Richard S. Hall677f5892007-07-10 15:29:11 +0000252 }
253
Karl Pauls0b9d26f2009-09-06 21:15:38 +0000254 public String getUsage()
Richard S. Hall677f5892007-07-10 15:29:11 +0000255 {
Karl Pauls0b9d26f2009-09-06 21:15:38 +0000256 return "mystart &lt;id&gt; [&lt;id&gt; ...]";
Richard S. Hall677f5892007-07-10 15:29:11 +0000257 }
258
Karl Pauls0b9d26f2009-09-06 21:15:38 +0000259 public String getShortDescription()
Richard S. Hall677f5892007-07-10 15:29:11 +0000260 {
Karl Pauls0b9d26f2009-09-06 21:15:38 +0000261 return "start bundle(s).";
Richard S. Hall677f5892007-07-10 15:29:11 +0000262 }
263
Karl Pauls0b9d26f2009-09-06 21:15:38 +0000264 public void execute(String s, PrintStream out, PrintStream err)
Richard S. Hall677f5892007-07-10 15:29:11 +0000265 {
Karl Pauls0b9d26f2009-09-06 21:15:38 +0000266 StringTokenizer st = new StringTokenizer(s, " ");
Richard S. Hall677f5892007-07-10 15:29:11 +0000267
Karl Pauls0b9d26f2009-09-06 21:15:38 +0000268 // Ignore the command name.
269 st.nextToken();
Richard S. Hall677f5892007-07-10 15:29:11 +0000270
Karl Pauls0b9d26f2009-09-06 21:15:38 +0000271 // There should be at least one bundle id.
272 if (st.countTokens() &gt;= 1)
Richard S. Hall677f5892007-07-10 15:29:11 +0000273 {
Karl Pauls0b9d26f2009-09-06 21:15:38 +0000274 while (st.hasMoreTokens())
Richard S. Hall677f5892007-07-10 15:29:11 +0000275 {
Karl Pauls0b9d26f2009-09-06 21:15:38 +0000276 String id = st.nextToken().trim();
Richard S. Hall677f5892007-07-10 15:29:11 +0000277
Karl Pauls0b9d26f2009-09-06 21:15:38 +0000278 try {
279 long l = Long.valueOf(id).longValue();
Richard S. Hall677f5892007-07-10 15:29:11 +0000280 Bundle bundle = m_context.getBundle(l);
Karl Pauls0b9d26f2009-09-06 21:15:38 +0000281 if (bundle != null)
Richard S. Hall677f5892007-07-10 15:29:11 +0000282 {
283 bundle.start();
284 }
Karl Pauls0b9d26f2009-09-06 21:15:38 +0000285 else
Richard S. Hall677f5892007-07-10 15:29:11 +0000286 {
Karl Pauls0b9d26f2009-09-06 21:15:38 +0000287 err.println("Bundle ID " + id + " is invalid.");
Richard S. Hall677f5892007-07-10 15:29:11 +0000288 }
Karl Pauls0b9d26f2009-09-06 21:15:38 +0000289 } catch (NumberFormatException ex) {
290 err.println("Unable to parse id '" + id + "'.");
291 } catch (BundleException ex) {
292 if (ex.getNestedException() != null)
Richard S. Hall677f5892007-07-10 15:29:11 +0000293 err.println(ex.getNestedException().toString());
Karl Pauls0b9d26f2009-09-06 21:15:38 +0000294 else
Richard S. Hall677f5892007-07-10 15:29:11 +0000295 err.println(ex.toString());
Karl Pauls0b9d26f2009-09-06 21:15:38 +0000296 } catch (Exception ex) {
Richard S. Hall677f5892007-07-10 15:29:11 +0000297 err.println(ex.toString());
298 }
299 }
300 }
Karl Pauls0b9d26f2009-09-06 21:15:38 +0000301 else
Richard S. Hall677f5892007-07-10 15:29:11 +0000302 {
Karl Pauls0b9d26f2009-09-06 21:15:38 +0000303 err.println("Incorrect number of arguments");
Richard S. Hall677f5892007-07-10 15:29:11 +0000304 }
305 }
Karl Pauls0b9d26f2009-09-06 21:15:38 +0000306}</textarea>
307<script class="javascript">
308 if(!window.newcodemacro_initialised)
309 {
310 window.newcodemacro_initialised = true;
311 window.oldonloadmethod = window.onload;
312 window.onload = function(){
313 dp.SyntaxHighlighter.HighlightAll('newcodemacro');
314 if(window.oldonloadmethod)
315 {
316 window.oldonloadmethod();
317 }
318 }
319 }
320
321</script>
322</div>
323
Richard S. Hall677f5892007-07-10 15:29:11 +0000324
325<p>A bundle activator class is needed for packaging the command servce; the bundle activator registers the command service in its <tt>start()</tt> method. Note: You do not need one activator per command, a single activator can register any number of commands.</p>
326
Karl Pauls0b9d26f2009-09-06 21:15:38 +0000327<div class="code">
328<textarea name="newcodemacro" class="java:nocontrols:nogutter" rows="10" readonly="readonly">package test;
Richard S. Hall677f5892007-07-10 15:29:11 +0000329
Karl Pauls0b9d26f2009-09-06 21:15:38 +0000330import org.osgi.framework.BundleActivator;
331import org.osgi.framework.BundleContext;
Richard S. Hall677f5892007-07-10 15:29:11 +0000332
Karl Pauls0b9d26f2009-09-06 21:15:38 +0000333public class MyStartActivator implements BundleActivator
Richard S. Hall677f5892007-07-10 15:29:11 +0000334{
Karl Pauls0b9d26f2009-09-06 21:15:38 +0000335 private transient BundleContext m_context = null;
Richard S. Hall677f5892007-07-10 15:29:11 +0000336
Karl Pauls0b9d26f2009-09-06 21:15:38 +0000337 public void start(BundleContext context)
Richard S. Hall677f5892007-07-10 15:29:11 +0000338 {
339 m_context = context;
340
Karl Pauls0b9d26f2009-09-06 21:15:38 +0000341 // Register the command service.
342 context.registerService(
Richard S. Hall677f5892007-07-10 15:29:11 +0000343 org.apache.felix.shell.Command.class.getName(),
Karl Pauls0b9d26f2009-09-06 21:15:38 +0000344 new MyStartCommandImpl(m_context), null);
Richard S. Hall677f5892007-07-10 15:29:11 +0000345 }
346
Karl Pauls0b9d26f2009-09-06 21:15:38 +0000347 public void stop(BundleContext context)
Richard S. Hall677f5892007-07-10 15:29:11 +0000348 {
Karl Pauls0b9d26f2009-09-06 21:15:38 +0000349 // Services are automatically unregistered so
350 // we don't have to unregister the factory here.
351 }
352}</textarea>
353<script class="javascript">
354 if(!window.newcodemacro_initialised)
355 {
356 window.newcodemacro_initialised = true;
357 window.oldonloadmethod = window.onload;
358 window.onload = function(){
359 dp.SyntaxHighlighter.HighlightAll('newcodemacro');
360 if(window.oldonloadmethod)
361 {
362 window.oldonloadmethod();
363 }
364 }
365 }
366
367</script>
368</div>
369
Richard S. Hall677f5892007-07-10 15:29:11 +0000370
371<p>To compile these classes you will need to have the <tt>framework.jar</tt> file on your class path. Compile all of the source files using a command like:</p>
372
Karl Pauls0b9d26f2009-09-06 21:15:38 +0000373<div class="preformatted panel" style="border-width: 1px;"><div class="preformattedContent panelContent">
Richard S. Hall677f5892007-07-10 15:29:11 +0000374<pre>java -d c:\classes *.java
375</pre>
376</div></div>
377
378<p>This command compiles all of the source files and outputs the generated class files into a subdirectory of the <tt>c:\classes</tt> directory, called test, named after the package of the source files; for the above command to work, the <tt>c:\classes</tt>
379directory must exist. Once you have compiled all of the above classes,
380you need to create a bundle JAR file of the generated package
381directory. The bundle JAR file needs a manifest, so create a file
382called <tt>manifest.mf</tt> with the following contents:</p>
383
Karl Pauls0b9d26f2009-09-06 21:15:38 +0000384<div class="code">
385<textarea name="newcodemacro" class="java:nocontrols:nogutter" rows="10" readonly="readonly">Bundle-Name: My Start Command
386Bundle-Description: A 'start' command for the shell service.
Richard S. Hall677f5892007-07-10 15:29:11 +0000387Bundle-Activator: test.MyStartActivator
388Bundle-ClassPath: .
Karl Pauls0b9d26f2009-09-06 21:15:38 +0000389Import-Package: org.apache.felix.shell</textarea>
390<script class="javascript">
391 if(!window.newcodemacro_initialised)
392 {
393 window.newcodemacro_initialised = true;
394 window.oldonloadmethod = window.onload;
395 window.onload = function(){
396 dp.SyntaxHighlighter.HighlightAll('newcodemacro');
397 if(window.oldonloadmethod)
398 {
399 window.oldonloadmethod();
400 }
401 }
402 }
403
404</script>
405</div>
406
Richard S. Hall677f5892007-07-10 15:29:11 +0000407
408<p>To create the bundle JAR file, issue the command:</p>
409
Karl Pauls0b9d26f2009-09-06 21:15:38 +0000410<div class="preformatted panel" style="border-width: 1px;"><div class="preformattedContent panelContent">
Richard S. Hall677f5892007-07-10 15:29:11 +0000411<pre>jar cfm mystart.jar manifest.mf -C c:\classes test
412</pre>
413</div></div>
414
415<p>This command creates a JAR file using the manifest you created and
416includes all of the classes in the test directory inside of the <tt>c:\classes</tt>
417directory. Once the bundle JAR file is created, you are ready to add
418the command service to the shell service; simply start Felix and
419install and start the bundle created by the above command. By doing so,
420the new <tt>mystart</tt> command is made available via the shell service.</p>
421
422<p><a name="ApacheFelixShellService-security"></a></p>
423
424<h2><a name="ApacheFelixShellService-SecurityandtheShellService"></a>Security and the Shell Service</h2>
425
426<p>The shell service security handling is quite simple, all security is
427handled by the standard OSGi framework mechanisms. For example, if a
428bundle should not be able to register a shell service, then it should
429not be given the corresponding service permission. Security handling
430may change in future release after some experience is gained through
431usage.</p>
432
433<p><a name="ApacheFelixShellService-feedback"></a></p>
434
435<h2><a name="ApacheFelixShellService-Feedback"></a>Feedback</h2>
436
Karl Pauls0b9d26f2009-09-06 21:15:38 +0000437<p>Subscribe to the Felix users mailing list by sending a message to <a href="mailto:users-subscribe@felix.apache.org" rel="nofollow">users-subscribe@felix.apache.org</a>; after subscribing, email questions or feedback to <a href="mailto:users@felix.apache.org" rel="nofollow">users@felix.apache.org</a>.</p>
Richard S. Hall677f5892007-07-10 15:29:11 +0000438 </div>
Karl Pauls0b9d26f2009-09-06 21:15:38 +0000439 </body></html>