Version 4.1 by Pascal Robert on 2007/07/08 18:52

Show last authors
1 This article was written by Andrew Lindesay ([[http://www.lindesay.co.nz]]) around May 2005. It first appeared as LaTeX PDF and has been transcribed into this Wiki. You use the information contained in this document at your own risk. Please contact the author if you feel there may have been an error in the conversion to Wiki markup.
2
3 {{toc}}{{/toc}}
4
5 = Abstract =
6
7 From WebObjects 5.2, it has been possible to derive a build product from a WebObjects application project that can be deployed into a J2EE servlet container. This article shows how it is possible to
8 deploy a WebObjects 5.2 application into a Tomcat environment and achieve a very similar topology to the "native" WebObjects deploy.
9
10 = Introduction =
11
12 This document was originally written assuming a Tomcat 5 deployment, but after some difficulties with web services and AXIS, I have modified this document to also cater for a Tomcat 3 deployment. This document covers both circumstances.
13
14 = Assumptions =
15
16 This article assumes the following:
17
18 * WebObjects 5.2 (likely to work fine with newer versions)
19 * Java 1.4
20 * Latest Tomcat release of version 5 (5.5.12 at the time of writing) or 3 (3.3.2 at the time of writing)
21 * Some sort of UNIX deployment.
22 * The reader has some conception of the concepts behind servlet technology.
23 * The reader is familiar with a standard WebObjects deployment topology which will be referred to as //wotaskd// deployment.
24
25 For the purposes of this document, it is assumed that Tomcat has been installed at a directory on the local disc called ##computer code$TOMCATDIR##. It is also assumed that you will have another directory with the files required to configure and run an instance called ##computer code$INSTDIR##. It is assumed also that you will have a directory called ##computer code$JKDIR## with the Tomcat apache adaptor in it. In this article, some configuration files require the paths to be shown and these are tabulated below.
26
27 <center>
28
29 | ##computer code ##| $TOMCATDIR## ##
30 | ##computer code ##| /opt/tomcat## ##
31 |
32 | ##computer code ##| $INSTDIR## ##
33 | ##computer code ##| /opt/fooapp## ##
34 |
35 | ##computer code ##| $JKDIR## ##
36 | ##computer code ##| /opt/modjk## ##|
37 </center>
38
39 In reality, these directories could be located anywhere.
40
41 == Objective ==
42
43 The objective of this article is to show that a WebObjects application can be deployed into a servlet container and keep some of the desirable attributes of a WebObjects deployment topology. Some of these traits are itemised below.
44
45 * Clustering over a number of hardware nodes to prevent system downtime from a single hardware failure incident.
46 * Clustering over a number of virtual machine //instances// on each hardware node to avoid downtime from a single software failure incident.
47 * Ability to make efficient use of lower cost server hardware rather than encouraging use of large, expensive servers.
48 * Make maximum use of memory availabile in each virtual machine as cache to minimise database traffic and lower stress on the database server.
49 * Ability to make sessions "sticky" to a given virtual machine //instance//, whilst being multiplexed through a single web server front-end adaptor.
50 * Ability to load-balance requests to instances which are operational.
51
52 A standard WebObjects deployment topology is shown in the figure below alongside what is to be achieved with Tomcat. A typical J2EE deployment may have a different topology from this.
53
54 [[image:wo-tomcat-deploy-topology.gif||width="32" height="32"]]
55
56 = Servlet Build Products =
57
58 Creating a servlet build product from a WebObjects application project is covered in some depth by documentation that is supplied by Apple for WebObjects. This is not going to be repeated here, but here is a brief overview of the process.
59
60 * Include the ##computer codeJavaWOJSPServlet.framework## framework into your project.
61 * In the build settings, set the ##computer codeSERVLET//SINGLE//DIR//DEPLOY//##// value to ##computer codeYES## to create the most trouble-free form of deployment servlet.//
62 * Edit the ##computer codeSERVLET//DEPLOY//LICENSE## to contain your valid deployment license key if you need one for the version of WebObjects you are using.
63 * Edit the ##computer codeSERVLET//WEBAPPS//DIR## to point to ##computer code$INSTDIR/webapps/## or some place where you want the build product to go.
64
65 Now when you choose a //Deployment// build, you will also get the servlet assembled. The end result is a directory structure similar to that shown in the figure below.
66
67 [[image:wo-tomcat-ssd-filelayout-3.gif||width="32" height="32"]]
68
69 = Removing the DOCTYPE from .plist Files =
70
71 Many property-list files (often called plist files) have a document type at the top. This can refer to files on a MacOS-X machine or to files on Apple servers. In either case this can cause problems with deployments which are not on MacOS-X servers. The following script can be run with the argument of the ##computer codeWEB-INF## folder to remove these. The WebObjects application runs fine without this information in the plist files. This script can easily be incorporated one way or another as a step in the build process for your WebObjects project.
72
73 {{code}}
74
75 # [apl 3.may.2006]
76 # This will remove any DOCTYPE's from the top of plists so that
77 # they do not attempt to validate the DTD which is either
78 # extracted from /System or the internet over HTTP.
79
80 if [ -z $1 ]; then
81 {panel}
82 echo "syntax: stripdocype.sh <directory>"
83 exit 1
84 {panel}
85 fi
86
87 for PLISTFILE in `find $1 -name *.plist`
88 do
89 {panel}
90 sed \
91 '/<!DOCTYPE [^>]*>/s/.*//' \
92 $PLISTFILE \
93 > /tmp/remove-plist-temp
94 {panel}
95
96 cp /tmp/remove-plist-temp $PLISTFILE
97 done
98
99 {{/code}}
100
101 = Application Configuration with Tomcat =
102
103 A WebObjects project with servlet support has a file called ##computer codeweb.xml.template## in it. By default, this is located in ##computer code/Resources/Servlet Resources/WEB-INF## in your project. The ##computer codeweb.xml.template## file is used as a template for creating ##computer codeweb.xml## which is also known as the ##computer codeservlet deployment descriptor##. This deployment descriptor is used as the means of communicating settings to the application as well as the servlet container in which the application runs. This section covers some common changes to that file as well as a discussion around a means of general configuration of the application when it is running inside a servlet container.
104
105 == Data Source for Model Database Configuration ==
106
107 Some WebObjects engineers use the ##computer codesetConnectionDictionary(...)## method on a model to set the JDBC database connection parameters for the model. However, the servlet container has it's own ##computer codedata source## mechanism for supplying database information which will override any connection dictionary information which is set into the model. If you don't want this to happen, and you want your ##computer codesetConnectionDictionary(...)## to take effect, comment out the ##computer coderesource-ref## item with the title ##computer codejdbc/DefaultDataSource## in the ##computer codeweb.xml.template## file.
108
109 == Serving WebServerResources ==
110
111 To stop the web server resources (images, CSS files and other static data) from being served out of the java environment, you need to configure the ##computer codecontext-param## with the name ##computer codeWOAppMode## to be ##computer codeDeployment## in the ##computer codeweb.xml.template## file.
112
113 == Application Specific Configuration ==
114
115 This area of configuration covers items such as the following ficticious examples;
116
117 1. Email address of person to contact when system fails.
118 1. Frequency of polling some resource.
119 1. Optional connection information for databases.
120 1. GST rate for New Zealand.
121
122 In other words, these are application-specific configuration values. One way to configure your application specific parameters in a servlet container is to load your config into ##computer codeenv-entry##s in your ##computer codeweb.xml## file. Here is such an example of one such entry;
123
124 {{code}}
125
126 <env-entry>
127 <env-entry-name>foo/nz.co.foo.FooAppMailFrom</env-entry-name>
128 <env-entry-value>bar@foo.co.nz</env-entry-value>
129 <env-entry-type>java.lang.String</env-entry-type>
130 </env-entry>
131
132 {{/code}}
133
134 You can retrieve these value inside the application with some code as shown below. See the LEConfig class from LEWOStuff, the WebObjects framework from Lindesay Electric for an example. LEWOStuff also comes with a tool to help load standard java properties files into the servlet deployment descriptor.
135
136 {{code}}
137
138 import javax.naming.*;
139
140 // ...later in the same class...
141
142 Object valueO = null;
143
144 try
145 {
146 {panel}
147 InitialContext context = new InitialContext();
148 valueO = context.lookup("java:comp/env/foo/nz.co.foo.FooAppMailFrom");
149 {panel}
150 }
151 catch(javax.naming.NamingException ne)
152 { /* handle gracefully */ }
153
154 {{/code}}
155
156 It is probably easiest to apply these settings in some automated fashion to the ##computer codeweb.xml## file as part of a further automated build or deploy process.
157
158 == Application Binary ==
159
160 Put your application servlet build product at ##computer code$INSTDIR## such that the following path exists.
161
162 {{code}}
163
164 $INSTDIR/webapps/FooApp/WEB-INF
165
166 {{/code}}
167
168 == Tomcat Server Configuration Files ==
169
170 You need to create a tomcat configuration file for each of the instances that you would like to have. The pattern is followed here of having an instance number proceeded by the lower case letter "i". Put the first server configuration file at the following location.
171
172 {{code}}
173
174 $INSTDIR/server_i1.xml
175
176 {{/code}}
177
178 Here is an example of how this file might look. There is no coverage of the individual settings here as the reader is expected to review the tomcat documentation to discover the specific meanings of these settings.
179
180 === Tomcat 5 ===
181
182 {{code}}
183
184 <Server port="7071" shutdown="SHUTDOWN">
185 {panel}
186 <Service name="Catalina">
187 <Connector port="8081" maxHttpHeaderSize="8192"
188 maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
189 enableLookups="false" acceptCount="100"
190 connectionTimeout="20000" disableUploadTimeout="true" />
191 {panel}
192
193 <Connector port="9091" enableLookups="false" protocol="AJP/1.3" />
194
195 {panel}
196 <Engine name="i1" defaultHost="appserver1.foo.co.nz" jvmRoute="i1">
197 <Host name="appserver1.foo.co.nz"
198 appBase="/home/fooapp/webapps"
199 unpackWARs="true" autoDeploy="false"
200 xmlValidation="false" xmlNamespaceAware="false">
201 <Context cookies="false" docBase="FooApp"
202 path="FooApp" reloadable="false">
203 <Manager distributable="false" />
204 </Context>
205 </Host>
206 </Engine>
207 {panel}
208
209 </Service>
210 </Server>
211
212 {{/code}}
213
214 === Tomcat 3 ===
215
216 {{code}}
217
218 <?xml version="1.0" encoding="ISO-8859-1"?>
219
220 <Server>
221 <ContextManager workDir="work">
222
223 <LoaderInterceptor11 useApplicationLoader="true" />
224
225 <AutoDeploy source="modules" target="modules" redeploy="true" />
226 <AutoWebApp dir="modules" host="DEFAULT" trusted="true"/>
227 <AutoWebApp dir="/home/fooapp/webapps" trusted="true" reloadable="false" />
228
229 <SimpleMapper1 />
230
231 <SessionExpirer checkInterval="60" />
232 <SessionIdGenerator randomClass="java.security.SecureRandom" />
233
234 <WebXmlReader validate="false" />
235 <ErrorHandler showDebugInfo="true" />
236
237 <Jdk12Interceptor />
238 <LoadOnStartupInterceptor />
239 <Servlet22Interceptor />
240
241 <SessionId cookiesFirst="false" noCookies="true" />
242 <SimpleSessionStore maxActiveSessions="256" />
243
244 <Http10Connector port="8081" secure="false" />
245 <Ajp13Connector port="9091" tomcatAuthentication="false" shutdownEnable="true" />
246
247 </ContextManager>
248 </Server>
249
250 {{/code}}
251
252 Assuming that there will be three instances in this example deploy, this entire file should be replicated and modified twice for the other two instances. For the other instances' server configuration files, change the "i1" (Tomcat 5 only) by modifying the numerical component and change the port numbers by making the last digit the instance number. For example, the ports ##computer code7071##, ##computer code8081## and ##computer code9091## are used here. For "i2", use ##computer code7072##, ##computer code8082## and ##computer code9092##.
253
254 You should have three files now present called ##computer codeserver//i1.xml//##//, ##computer codeserver##//##i2.xml## and ##computer codeserver//i3.xml//##// in the directory ##computer code$INSTDIR##.//
255
256 == Startup An Instance ==
257
258 To startup an instance, issue a command as follows. You should issue this command for each of the server configuration files. The ##computer code$JAVA//HOME//##// shell environment variable should have been setup correctly before launching an instance.//
259
260 === Tomcat 5 ===
261
262 {{code}}
263
264 $TOMCATDIR/bin/startup.sh -config $INSTDIR/server_i1.xml
265
266 {{/code}}
267
268 === Tomcat 3 ===
269
270 {{code}}
271
272 $TOMCATDIR/bin/startup -config $INSTDIR/server_i1.xml -home $TOMCATDIR
273
274 {{/code}}
275
276 === Tomcat 3 Environment Variables ===
277
278 To pass java environment variables to your application, set the ##computer codeTOMCAT//OPTS//##// shell environment variable before starting up the Tomcat 3 environment. An example of this would be as follows;//
279
280 {{code}}
281
282 TOMCAT_OPTS=-Dabc=xyz
283 export TOMCAT_OPTS
284
285 {{/code}}
286
287 == Check Availability ==
288
289 You can now see if your instance is up using the following URL.
290
291 {{code}}
292
293 http://appserver1.foo.co.nz:8081/FooApp/WebObjects/FooApp.woa
294
295 {{/code}}
296
297 Note that both the AJP (This is the protocol between the apache adaptor and the individual Tomcat instances.) and the regular HTTP engines are accessing the same running application. This means that a test straight onto the application via HTTP is actually testing the application that is being accessed via AJP. This behaviour provides an opportunity to be able to monitor specific instances of Tomcat over direct HTTP.
298
299 Check each of your instances.
300
301 == Problems ==
302
303 Under Tomcat 5, check the log file at ##computer code$TOMCATHOME/logs/catalina.out## if you are unable to access your instance.
304
305 == Shutdown An Instance ==
306
307 To shutdown an instance, issue a command as follows.
308
309 === Tomcat 5 ===
310
311 {{code}}
312
313 $TOMCATDIR/bin/shutdown.sh -config $INSTDIR/server_i1.xml
314
315 {{/code}}
316
317 It is also possible to use the UNIX ##computer codetelnet## command to connect to the port described in the server configuration file's ##computer codeServer## tag and type the word supplied in the ##computer codeshutdown## attribute to take out the Tomcat instance.
318
319 === Tomcat 3 ===
320
321 {{code}}
322
323 TOMCATDIR/bin/shutdown -ajp13 -port 9091
324
325 {{/code}}
326
327 = Apache Adaptor Setup =
328
329 A final deployment usually involves apache feeding inbound requests to instances and handling situations where an instance has gone down and balancing load over the instances that are currently running. This job is undertaken by ##computer codemod//jk//##// which is a module for apache written by the Tomcat group. This module communicates with the tomcat instances using a protocol called ##computer codeAJP##. This protocol carries all the information required to check instances are operational as well as relaying requests to the instances. It is assumed that the configuration as well as binaries for the ##computer codemod##//##jk## apache module will be located at ##computer code$JKDIR##.
330
331 == Compiling and Installing ==
332
333 Instructions for downloading and installing ##computer codemod//jk//##// can be obtained from the Tomcat website. The ##computer codemod##//##jk.so## binary should be located at the following path.
334
335 {{code}}
336
337 $JKDIR/mod_jk.so
338
339 {{/code}}
340
341 Add a line to your system's apache httpd configuration file like this:
342
343 {{code}}
344
345 Include /opt/modjk/apache.conf
346
347 {{/code}}
348
349 Edit this file to look like this:
350
351 {{code}}
352
353 LoadModule jk_module /opt/modjk/mod_jk.so
354 AddModule mod_jk.c
355
356 JkLogFile /opt/modjk/mod_jk.log
357 JkLogLevel info
358
359 JkWorkerProperty worker.list=i1,i2,i3,loadbalancer
360 JkWorkerProperty worker.i1.type=ajp13
361 JkWorkerProperty worker.i1.port=9091
362 JkWorkerProperty worker.i1.host=appserver1.foo.co.nz
363 JkWorkerProperty worker.i1.lbfactor=1
364 JkWorkerProperty worker.i2.type=ajp13
365 JkWorkerProperty worker.i2.port=9092
366 JkWorkerProperty worker.i2.host=appserver1.foo.co.nz
367 JkWorkerProperty worker.i2.lbfactor=1
368 JkWorkerProperty worker.i3.type=ajp13
369 JkWorkerProperty worker.i3.port=9093
370 JkWorkerProperty worker.i3.host=appserver1.foo.co.nz
371 JkWorkerProperty worker.i3.lbfactor=1
372 JkWorkerProperty worker.loadbalancer.type=lb
373 JkWorkerProperty worker.loadbalancer.sticky_session=1
374 JkWorkerProperty worker.loadbalancer.local_worker_only=1
375 JkWorkerProperty worker.loadbalancer.balance_workers=i1,i2,i3
376
377 JkMount /FooApp/* loadbalancer
378
379 {{/code}}
380
381 Again, the details of the exact settings will not be covered in this article, but this should provide a simple guide to setting up this file to provide for a multi-instance deploy under Tomcat.
382
383 == Restart Apache ==
384
385 Now restart apache with the following command:
386
387 {{code}}
388
389 sudo apachectl restart
390
391 {{/code}}
392
393 == Test Application ==
394
395 Now you can test your application using a URL such as this one.
396
397 {{code}}
398
399 http://www.foo.co.nz/FooApp/WebObjects/FooApp.woa
400
401 {{/code}}
402
403 All three instances that you have setup should take some of the inbound requests.
404
405 == Choosing the Right Instance ==
406
407 ##computer codemod//jk//##// ensures that requests that have started a session will be directed to the correct instance where the session originated. This behaviour is known as
408 {t sticky sessions}
409 . It does not appear to be possible to nominate the instance from the servlet container in another way.//