platform-ui-home/rcp-proposal/rcp_tutorial/tutorial1.html

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.4 - (view) (download) (as text)

1 : jeem 1.1 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
2 :     <html>
3 :    
4 :     <head>
5 :     <title>Rich Client Platform</title>
6 :     <link href="default_style.css" rel=stylesheet>
7 :     </head>
8 :    
9 :     <body>
10 :    
11 :     <div align="right">
12 :     <font face="Times New Roman, Times, serif" size="2">Copyright © 2003-2004 Ed
13 :     Burnette.</font>
14 :     <table border="0" cellspacing="0" cellpadding="2" width="100%">
15 :     <tbody>
16 :     <tr>
17 :     <td align="left" valign="top" colspan="2" bgcolor="#0080c0"><b><font face="Arial,Helvetica"><font color="#ffffff">Eclipse
18 :     Article</font></font></b></td>
19 :     </tr>
20 :     </tbody>
21 :     </table>
22 :     </div>
23 :     <div align="left">
24 :     <h1 title="RCP Tutorial"><img src="images/Idea.jpg" align="middle" width="120" height="86"></h1>
25 :     </div>
26 :     <h1 align="center">Rich Client Tutorial Part 1 - Draft</h1>
27 :     <p class="summary">The Rich Client Platform (RCP) is an exciting new way to
28 :     build Java applications that can compete with native applications on any
29 :     platform. This tutorial is designed to get you started building RCP applications
30 :     quickly.</p>
31 :     <p><b>By Ed Burnette, SAS Institute Inc.</b><br>
32 : nick 1.4 <font size="-1">February 18, 2004 - Updated for 3.0M7, adding Troubleshooting section</font></p>
33 : jeem 1.1 <p><i>Note that Eclipse 3 is undergoing constant change so the steps you need to
34 :     take, the APIs, and the results may vary slightly (or not so slightly) from this
35 :     description.</i></p>
36 :     <hr width="100%">
37 :     <h2>Introduction</h2>
38 :     <p>Try this experiment: Show Eclipse to some friends or co-workers who haven't
39 :     seen it before and ask them to guess what language it is written in. Chances
40 :     are, they'll guess VB, C++, or C#, because those languages are used most often
41 :     for high quality client side applications. Then watch the look on their faces
42 :     when you tell them it was created in Java, especially if they are Java
43 :     programmers.</p>
44 :     <p>Because of its unique open source license, you can use the technologies that
45 :     went into Eclipse to create your own commercial quality programs With previous
46 :     version of Eclipse, this was possible but difficult, especially when you wanted
47 :     to heavily customize the menus, layouts, and other user interface elements. That
48 :     was because the &quot;IDE-ness&quot; of Eclipse was hard-wired into it. Version
49 :     3 introduces the Rich Client Platform, which is basically a refactoring of the
50 :     fundamental parts of Eclipse's UI, allowing it to be used for non-IDE
51 :     applications.</p>
52 :     <p>If you want to cut to the chase and look at the code you can <a href="RcpTutorial.zip">download
53 :     the Eclipse project here</a>. Otherwise, let's take a look at how to construct
54 :     an RCP application.</p>
55 :     <h2><a name="section_1"></a> Getting started</h2>
56 :     <p>RCP applications are based on the familiar Eclipse plug-in architecture, (if
57 :     it's not familiar to you, see the references section). Therefore, you'll need to
58 : nick 1.3 create a plug-in to be your main program.
59 :     Select New &gt; Project &gt; Plug-in
60 : jeem 1.1 Development &gt; Plug-in Project to bring up the Plug-in Project wizard. On the
61 :     subsequent pages, enter a Project name such as <b>org.eclipsepowered.rcptutorial1</b>,
62 : nick 1.3 indicate you want a Java project, and
63 :     enter a Plug-in Id (this should match the project name).
64 :     </p>
65 :     <p>
66 :     The generated plug-in class that you may be familiar with in previous
67 : jeem 1.1 releases is no longer required in Eclipse 3.0.
68 :     You can still have one to hold global data if you like, but for this
69 : nick 1.3 example just remove it altogether to save a little space.
70 :     To do this, turn off the option that says
71 :     Generate the Java class that controls the plug-ins life cycle.
72 :     Then Click on Finish to
73 :     generate the template.</p>
74 : jeem 1.1 <h2>The main program</h2>
75 : nick 1.3 <p>The main program for an application implements <code>IPlatformRunnable</code>,
76 :     which just has a method called
77 :     <code>run()</code>.
78 :     Listing 1 shows a simple implementation that shows you the minimum you have to
79 : jeem 1.1 do.</p>
80 :     <p><b>Listing 1. RcpApplication class.
81 :     </b></p>
82 :     <pre>
83 :     package org.eclipsepowered.rcptutorial1;
84 :     import org.eclipse.core.boot.IPlatformRunnable;
85 :     import org.eclipse.swt.widgets.Display;
86 :     import org.eclipse.ui.PlatformUI;
87 :     import org.eclipse.ui.application.WorkbenchAdvisor;
88 :    
89 :     public class RcpApplication implements IPlatformRunnable {
90 : nick 1.3 public Object run(Object args) {
91 :     WorkbenchAdvisor workbenchAdvisor = new RcpWorkbenchAdvisor();
92 :     Display display = PlatformUI.createDisplay();
93 :     int returnCode = PlatformUI.createAndRunWorkbench(display,
94 :     workbenchAdvisor);
95 :     if (returnCode == PlatformUI.RETURN_RESTART) {
96 :     return IPlatformRunnable.EXIT_RESTART;
97 :     } else {
98 :     return IPlatformRunnable.EXIT_OK;
99 :     }
100 :     }
101 : jeem 1.1 }
102 :     </pre>
103 :     <h2>Creating a default perspective</h2>
104 :     <p>
105 :     Next, you must define at least one perspective and make it the default.
106 : nick 1.3 Perspectives are created by implementing <code>IPerspectiveFactory</code> (see listing 2).
107 : jeem 1.1 The important part
108 : nick 1.3 of this interface is the <code>createInitialLayout()</code> method where you position and
109 : jeem 1.1 open any views and/or editors you'd like the user to start with.
110 :     In this example
111 :     we're not going to create any views so it will be a pretty boring perspective.
112 :     </p>
113 :    
114 :     <p><b>Listing 2. RcpPerspective class.
115 :     </b></p>
116 :     <pre>
117 :     package org.eclipsepowered.rcptutorial1;
118 :    
119 :     import org.eclipse.ui.IPageLayout;
120 :     import org.eclipse.ui.IPerspectiveFactory;
121 :    
122 :     public class RcpPerspective implements IPerspectiveFactory {
123 :    
124 : nick 1.3 public RcpPerspective() {
125 :     }
126 : jeem 1.1
127 : nick 1.3 public void createInitialLayout(IPageLayout layout) {
128 :     }
129 : jeem 1.1 }
130 :    
131 :     </pre>
132 :     <h2>Workbench Advisor</h2>
133 :     <p>
134 :     The Workbench Advisor class helps customize the workbench to add and subtract
135 :     toolbars, perspectives, and so forth.
136 :     This will covered in more detail in part 2 of the tutorial.
137 :     For now, the absolute minimum you have to do here
138 :     is define which perspective is the default one.
139 :     See listing 3 for the code.
140 :     </p>
141 :    
142 :     <p><b>Listing 3. RcpWorkbenchAdvisor class.
143 :     </b></p>
144 :     <pre>
145 :     package org.eclipsepowered.rcptutorial1;
146 :     import org.eclipse.ui.application.WorkbenchAdvisor;
147 :    
148 :     public class RcpWorkbenchAdvisor extends WorkbenchAdvisor {
149 :    
150 : nick 1.3 public String getInitialWindowPerspectiveId() {
151 :     return &quot;org.eclipsepowered.rcptutorial1.RcpPerspective&quot;;
152 :     }
153 : jeem 1.1 }
154 :     </pre>
155 :     <h2>Plug-in manifest</h2>
156 :     <p>As usual, the plug-in manifest, plugin.xml, ties everything together.
157 :     The class name in
158 :     the plugin tag refers to the plug-in class we deleted earlier,
159 :     so you should remove the reference too.
160 :     The main program
161 : nick 1.3 class name is defined with the <code>org.eclipse.core.runtime.applications</code> extension
162 :     and the perspective with <code>org.eclipse.ui.perspectives</code>.
163 : jeem 1.1 When you're done you should have something that looks like listing 4.
164 :     </p>
165 :    
166 :     <p><b>Listing 4. Plugin.xml.
167 :     </b></p>
168 :     <pre>
169 :     &lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
170 :     &lt;?eclipse version=&quot;3.0&quot;?&gt;
171 :     &lt;plugin
172 : nick 1.3 id=&quot;org.eclipsepowered.rcptutorial1&quot;
173 :     name=&quot;%pluginName&quot;
174 :     version=&quot;0.0.0&quot;
175 :     provider-name=&quot;%providerName&quot;&gt;
176 :    
177 :     &lt;runtime&gt;
178 :     &lt;library name=&quot;rcptutorial1.jar&quot;&gt;
179 :     &lt;export name=&quot;*&quot;/&gt;
180 :     &lt;/library&gt;
181 :     &lt;/runtime&gt;
182 :     &lt;requires&gt;
183 :     &lt;import plugin=&quot;org.eclipse.core.runtime.compatibility&quot;/&gt;
184 :     &lt;import plugin=&quot;org.eclipse.ui&quot;/&gt;
185 :     &lt;/requires&gt;
186 :    
187 :    
188 :     &lt;extension
189 :     id=&quot;RcpApplication&quot;
190 :     point=&quot;org.eclipse.core.runtime.applications&quot;&gt;
191 :     &lt;application&gt;
192 :     &lt;run
193 :     class=&quot;org.eclipsepowered.rcptutorial1.RcpApplication&quot;&gt;
194 :     &lt;/run&gt;
195 :     &lt;/application&gt;
196 :     &lt;/extension&gt;
197 :     &lt;extension
198 :     point=&quot;org.eclipse.ui.perspectives&quot;&gt;
199 :     &lt;perspective
200 :     name=&quot;%perspectiveName&quot;
201 :     class=&quot;org.eclipsepowered.rcptutorial1.RcpPerspective&quot;
202 :     id=&quot;org.eclipsepowered.rcptutorial1.RcpPerspective&quot;&gt;
203 :     &lt;/perspective&gt;
204 :     &lt;/extension&gt;
205 : jeem 1.1 &lt;/plugin&gt;
206 :     </pre>
207 :     <h2>Miscellaneous</h2>
208 :     <p>The build.properties file (see listing 5)
209 :     will be needed when exporting the application for
210 :     others to use.</p>
211 :    
212 :     <p><b>Listing 5. Build.properties.
213 :     </b></p>
214 :     <pre>
215 :     bin.includes = plugin.xml,\
216 :     *.jar,\
217 :     rcptutorial1.jar,\
218 :     plugin.properties
219 :     source.rcptutorial1.jar = src/
220 :     </pre>
221 :    
222 :     <p>
223 :     Finally, the plugin.properties file (listing 6) contains natural language strings at the
224 :     plug-in registry level (i.e., things the run-time has to know about your plug-in
225 :     before even loading it).
226 :     You could hard-code these into your plug-in manifest
227 :     but it's best to get into the i18n habit from the start.
228 :     </p>
229 :    
230 :     <p><b>Listing 6. Plugin.properties
231 :     </b></p>
232 :     <pre>
233 :     pluginName = RcpTutorial1 Plug-in
234 :     providerName = eclipsepowered.org
235 :     perspectiveName = RcpTutorial1
236 :     </pre>
237 :    
238 :     <h2>Taking it for a spin</h2>
239 :     <p>Normally, when testing a plug-in you would select the project and then select
240 :     Run &gt; Debug &gt; Debug As &gt; Run-time Workbench. Go ahead and try that now.
241 :     It will give you a full blown Eclipse IDE Workbench window because we haven't
242 :     overridden the application name - it is still defaulting to the IDE application.
243 : nick 1.3 </p>
244 :     <p>
245 : jeem 1.1 To fix this, edit the launch configuration you just created (select Run &gt;
246 :     Debug &gt; Debug..., and then select New_Configuration or create a new one). In
247 : nick 1.3 the Arguments tab, select <code>org.eclipsepowered.rcptutorial1.RcpApplication</code>
248 :     for the Application name.
249 :     This comes from the id specified on the
250 :     <code>org.eclipse.core.runtime.applications</code> extension point
251 :     (in this example, <code>RcpApplication</code>), prepended with the
252 :     plug-in id (<code>org.eclipsepowered.rcptutorial1</code>).
253 :     </p>
254 : jeem 1.1 <p>Now switch over to the Plug-ins and Fragments tab. Select the option to
255 : nick 1.3 Launch with this plug-in and all of its pre-requisites.
256 :     Enter the plug-in id (<code>org.eclipsepowered.rcptutorial1</code>)
257 :     and press Debug. You should see a bare-bones Workbench start up (see figure 1).</p>
258 : jeem 1.1
259 :     <p><b>Figure 1. World's simplest RCP application.
260 :     </b></p>
261 : nick 1.3 <img src="images/spin.png" width="404" height="270">
262 : jeem 1.1
263 :     <p>If you don't see this, you should be able to find an error message in the
264 :     run-time workbench's .log file (runtime-workspace/.metadata/.log). Simply bring
265 :     it up in a text editor and look for the most recent errors. You may find it
266 :     useful to delete the .log file before running the program so you won't get
267 :     confused by older messages.</p>
268 : nick 1.3 <p>
269 :     <img src="images/tip.gif" alt="Tip: " width="62" height="13">
270 :     Specify the <code>-consoleLog</code> option
271 :     in the Arguments tab under Program Arguments to have all error messages
272 :     sent to the Console. This is easier than searching around for the .log file.
273 :     </p>
274 : jeem 1.1
275 :     <h2>Running it outside of Eclipse</h2>
276 :     <p>The whole point of all this is to be able to run stand-alone applications
277 :     without the user having to know anything about the Java and Eclipse code being
278 :     used under the covers. For a real application you will probably want to provide
279 :     a self-contained executable generated by an install program like InstallShield.
280 :     That's really beyond the scope of this article though, so we'll do something
281 :     simpler.</p>
282 :     <p>We need to create a simplified version of the Eclipse install directory
283 :     because the Eclipse plug-in loader expects things to be in a certain layout.
284 :     Here are the steps to get started:</p>
285 :     <ol>
286 :     <li>Right click on the plug-in project and select Export.
287 :     <li>Select Deployable plug-ins and fragments and then press Next.
288 :     <li>Select the plug-in(s) that should be included. For this
289 :     example that would be org.eclipsepowered.rcptutorial1.
290 :     <li>Select the option to Export as a directory structure, and for the
291 :     directory enter a name ending with RcpTutorial1 (for example, C:\RcpTutorial1 on
292 :     Windows).
293 :     <li>If you want to provide source code for your application,
294 :     select the Include Source Code option.
295 :     <li>Press Finish to export the plug-in.
296 :     Eclipse will perform a full build in the background
297 :     and populate the directory for you.
298 :     </ol>
299 :     <p>To complete the RcpTutorial1 directory, copy the startup.jar file from the Eclipse
300 : nick 1.3 install directory into the top level, and copy all the org.eclipse plug-ins
301 :     that are required into the plugins directory.
302 :     </p>
303 :     <p>
304 :     Which ones are required, you ask?
305 : nick 1.4 You can find a partial answer by right-clicking on the project and selecting
306 : nick 1.3 PDE Tools > Open Dependencies.
307 :     Add all the plug-ins listed there, including all the ones they depend on (you can
308 :     see the list by fully expanding the tree).
309 :     </p>
310 :     <p>
311 : nick 1.4 In addition to these, you'll have to add any plug-ins that
312 :     are required at run-time but not at build time.
313 :     These may not be listed directly or indirectly in your plugin.xml file.
314 :     The only way to find them in current builds is to run the application
315 :     and see if it fails,
316 :     and add any required plug-ins until it stops failing.
317 :     Hopefully this will be improved in a future build.
318 :     See the Troubleshooting section for more information.
319 :     </p>
320 :     <p>
321 : nick 1.3 When you're
322 : jeem 1.1 done you should have a structure that looks like this:</p>
323 :     <pre>
324 : nick 1.3 RcpTutorial1
325 :     | startup.jar
326 :     +--- plugins
327 :     +--- org.eclipse.core.runtime.compatibility_3.0.0
328 :     +--- org.eclipse.core.runtime_3.0.0
329 :     +--- org.eclipse.help_3.0.0
330 :     +--- org.eclipse.jface_3.0.0
331 :     +--- org.eclipse.osgi.services_3.0.0
332 :     +--- org.eclipse.osgi.util_3.0.0
333 :     +--- org.eclipse.osgi_3.0.0
334 :     +--- org.eclipse.swt.win32_3.0.0
335 :     +--- org.eclipse.swt_3.0.0
336 :     +--- org.eclipse.ui.workbench_3.0.0
337 :     +--- org.eclipse.ui_3.0.0
338 :     +--- org.eclipse.update.configurator_3.0.0
339 :     +--- org.eclipsepowered.rcptutorial1
340 : jeem 1.1 </pre>
341 :     <p>That's all you need to run an RCP application, but it would be difficult for
342 :     anyone to use it without some kind of launching program. Eclipse uses
343 : nick 1.3 eclipse.exe, but for this example we'll just use a batch file.
344 :     Create a Windows command file
345 : jeem 1.1 called rcptutorial1.cmd and place it in the top level RcpTutorial1 directory. A Unix shell
346 :     script version would be similar.
347 :     <pre>
348 : nick 1.3 @echo off
349 :     setlocal
350 :     cd %~dp0
351 :     rem Check workspace\.metadata\.log for errors
352 :     start javaw -cp startup.jar org.eclipse.core.launcher.Main
353 :     -application org.eclipsepowered.rcptutorial1.RcpApplication %*
354 :     endlocal
355 : jeem 1.1
356 :     </pre>
357 :     <p>The start command should be all on one line. As before, the -application
358 :     option refers back to the id specified on the
359 :     org.eclipse.core.runtime.applications extension point.</p>
360 :     <p>You can get as fancy as you want in this script file. Here's a variant that I
361 :     like to use when debugging because it will display any error messages from the
362 :     Eclipse loader in a separate window:</p>
363 :     <pre>
364 : nick 1.3 echo on
365 :     setlocal
366 :     cd %~dp0
367 :     rem Display workspace\.metadata\.log if there are errors
368 :     del workspace\.metadata\.log
369 :     java -cp startup.jar org.eclipse.core.launcher.Main
370 :     -application org.eclipsepowered.rcptutorial1.RcpApplication %*
371 :     || type workspace\.metadata\.log &amp;&amp; pause
372 :     endlocal
373 : jeem 1.1 </pre>
374 :     <p>The java command should be all on one line.</p>
375 :     <p>To eliminate the startup window on Windows, you can use a shortcut instead of
376 :     a script file. Right click inside the RcpTutorial1 folder, select New &gt; Shortcut
377 :     and enter this as the location of the item (on one line):</p>
378 :     <pre>
379 : nick 1.3 %windir%\system32\javaw.exe -cp startup.jar org.eclipse.core.launcher.Main
380 :     -application org.eclipsepowered.rcptutorial1.RcpApplication
381 : jeem 1.1 </pre>
382 :     <p>Enter a descriptive name on the next page and then press Finish. Then try
383 :     double-clicking on your new shortcut to try it out. You may want to edit the
384 :     shortcut (right click on it and select Properties) to change the default working
385 :     directory.</p>
386 : nick 1.3 <p>
387 :     <img src="images/tip.gif" alt="Tip: " width="62" height="13">
388 :     For fully branding a program with a splash screen, custom icons, and so forth
389 :     you'll need to define a primary feature, some configuration files, and use
390 :     the eclipse.exe launcher.
391 :     See the online help under Platform Plug-in Developer Guide >
392 :     Programmer's Guide > Packaging and delivering Eclipse based products
393 :     for more information.
394 :     </p>
395 : nick 1.4
396 :     <h2>Troubleshooting</h2>
397 :     <p>
398 :     After I wrote this tutorial I started getting mail from people who couldn't
399 :     run it for one reason or another.
400 :     This section will collect the errors that they've seen and
401 :     the solutions.
402 :     Remember to use the <code>-consoleLog</code> command line option to see
403 :     extra diagnostics in the console window where you invoked your
404 :     RCP application.
405 :     </p>
406 :     <dl>
407 :     <dt>
408 :     <b>Launching failed because the following plug-ins are neither in the
409 :     workspace nor selected on the Target Platform preference page: ...</b>
410 :     <dd>
411 :     This one is a sanity check performed by PDE.
412 :     It's pretty self explanitory so just look at the list
413 :     provided, go to the Plug-ins tab of your launch configuration,
414 :     and put a checkmark next to all of the plug-ins listed
415 :     in the error.
416 :    
417 :     <dt>
418 :     <b>!MESSAGE Unable to locate application extension: org.eclipsepowered.rcptutorial1.RcpApplication</b>
419 :     <dd>
420 :     First, check to make sure that the plug-in containing your
421 :     application is included in your launch configuration.
422 :     Next, check the spelling, and remember that the application name
423 :     is the plug-in id followed by a period and the id you specified on the
424 :     <code>org.eclipse.core.runtime.applications</code> extension.
425 :     This message might also be printed if all the required
426 :     plug-ins are not available at run-time (though in M7 and later
427 :     you're supposed to get a different error for this case).
428 :     <dt>
429 :     <b>!MESSAGE Bundle reference:file:C:/eclipse/plugins/org.eclipse.ui_3.0.0 [13] was not resolved</b>
430 :     <dd>
431 :     This one is a little tougher.
432 :     It usually means that some prerequisite of the plug-in listed was not there
433 :     You can find out exactly what's going on by re-running the program
434 :     with the <code>-console</code> command line option.
435 :     This will allow you to enter OSGi console commands to diagnose the problem.
436 :     After the error occurs again, go to the console window and enter the
437 :     <code>ss</code> command.
438 :     You'll see one or more bundles which were installed but not resolved.
439 :     Pick one of them (in the above message it would be number 13), and enter the
440 :     <code>diag <i>nnn</i></code> command, where <i>nnn</i>
441 :     is the bundle number. For example, "<b>diag 13</b>".
442 :     This will tell you what the missing bundle is.
443 :     Note that you can't run OSGi commands if the program has already terminated.
444 :     Future builds will have better error messages so you don't have to
445 :     go through all this - they'll just tell you what's missing.
446 :     </dl>
447 :    
448 : nick 1.3
449 : jeem 1.1 <h2>Conclusion</h2>
450 :     <p>In part 1 of this tutorial, we looked at what is necessary to create a
451 :     bare-bones Rich Client application. The next part will delve into customizations
452 :     using the WorkbenchAdvisor class.
453 :     All the sample code may be <a href="RcpTutorial.zip">downloaded
454 :     here</a>.
455 :     </p>
456 :    
457 :     <h2>References</h2>
458 :     <p><a href="../rich_client_platform_facilities.html">Rich Client Platform
459 :     Facilities</a><br>
460 :     <a href="http://bugs.eclipse.org/bugs/show_bug.cgi?id=36967">Bug 36967 - [Plan
461 :     Item] Enable Eclipse to be used as a rich client platform</a><br>
462 : nick 1.2 <a href="http://dev.eclipse.org/viewcvs/index.cgi/%7Echeckout%7E/org.eclipse.ui.examples.rcp.browser/readme.html">RCP Browser example
463 :     (project org.eclipse.ui.examples.rcp.browser)</a><br>
464 : jeem 1.1 <a href="http://www.eclipse.org/articles/Article-PDE-does-plugins/PDE-intro.html">PDE
465 :     Does Plug-ins</a><br>
466 :     <a href="http://www.eclipse.org/articles/Article-Internationalization/how2I18n.html">How
467 :     to Internationalize your Eclipse Plug-in</a><br>
468 :     <a href="http://www.eclipse.org/articles/Article-Plug-in-architecture/plugin_architecture.html">Notes
469 :     on the Eclipse Plug-in Architecture</a><br>
470 :     </p>
471 :     <p><small>IBM is trademark of International Business Machines Corporation in the
472 :     United States, other countries, or both.</small></p>
473 :     <p><small>Java and all Java-based trademarks and logos are trademarks or
474 :     registered trademarks of Sun Microsystems, Inc. in the United States, other
475 :     countries, or both.</small></p>
476 :     <p><small>Microsoft and Windows are trademarks of Microsoft Corporation in the
477 :     United States, other countries, or both.</small></p>
478 :     <p><small>Other company, product, and service names may be trademarks or service
479 :     marks of others.</small></p>
480 :    
481 :     </body>
482 :    
483 :     </html>